# PaCkAgE DaTaStReAm
opensvc 1 14384
# end of header
                                                                                                                                                                                                                                                                                                                                                                                                                                                                           0707010005c703000081a40000000000000000000000015ab2183a000000d3000000660000000000000000000000000000001000000000opensvc/pkginfo   CLASSES=none
PSTAMP=OPENSVC-1.9,REV=1489
EMAIL=support@opensvc.com
VENDOR=http://www.opensvc.com
CATEGORY=application
VERSION=1.9,REV=1489
ARCH=all
NAME=Automation and configuration management agent
PKG=opensvc
 0707010005c702000081a40000000000000000000000015ab2183a0000e276000000660000000000000000000000000000000f00000000opensvc/pkgmap    : 1 14384
1 d none /etc/bash_completion.d 0755 root root
1 f none /etc/bash_completion.d/opensvc 0664 root root 30270 11459 1521620820
1 d none /etc/opensvc 0755 root root
1 s none /usr/bin/nodemgr=../share/opensvc/bin/opensvc
1 s none /usr/bin/svcmgr=../share/opensvc/bin/opensvc
1 s none /usr/bin/svcmon=../share/opensvc/bin/opensvc
1 d none /usr/share 0755 root sys
1 d none /usr/share/doc 0755 root other
1 d none /usr/share/doc/opensvc 0755 root root
1 f none /usr/share/doc/opensvc/AUTHORS 0664 root root 506 47637 1521620820
1 f none /usr/share/doc/opensvc/auth.conf 0664 root root 7306 56277 1521620820
1 f none /usr/share/doc/opensvc/copyright 0664 root root 2320 63869 1521620820
1 d none /usr/share/doc/opensvc/provisioning 0755 root root
1 f none /usr/share/doc/opensvc/provisioning/provisioning.agent.debian 0775 root root 2602 50922 1521620820
1 f none /usr/share/doc/opensvc/provisioning/provisioning.example 0664 root root 362 30449 1521620820
1 f none /usr/share/doc/opensvc/schedule 0664 root root 2290 30408 1521620820
1 f none /usr/share/doc/opensvc/template.comp_module.py 0775 root root 1232 29792 1521620820
1 f none /usr/share/doc/opensvc/template.comp_module.sh 0775 root root 419 29995 1521620820
1 f none /usr/share/doc/opensvc/template.node.arbitrator.conf.gz 0664 root root 699 21495 1521620820
1 f none /usr/share/doc/opensvc/template.node.asset.conf.gz 0664 root root 226 26587 1521620820
1 f none /usr/share/doc/opensvc/template.node.brocade.conf.gz 0664 root root 221 27994 1521620820
1 f none /usr/share/doc/opensvc/template.node.centera.conf.gz 0664 root root 219 26771 1521620820
1 f none /usr/share/doc/opensvc/template.node.checks.conf.gz 0664 root root 227 28795 1521620820
1 f none /usr/share/doc/opensvc/template.node.cluster.conf.gz 0664 root root 669 17026 1521620820
1 f none /usr/share/doc/opensvc/template.node.cni.conf.gz 0664 root root 232 27157 1521620820
1 f none /usr/share/doc/opensvc/template.node.compliance.conf.gz 0664 root root 471 55838 1521620820
1 f none /usr/share/doc/opensvc/template.node.dcs.conf.gz 0664 root root 217 25434 1521620820
1 f none /usr/share/doc/opensvc/template.node.dequeue_actions.conf.gz 0664 root root 227 27275 1521620820
1 f none /usr/share/doc/opensvc/template.node.disks.conf.gz 0664 root root 225 27300 1521620820
1 f none /usr/share/doc/opensvc/template.node.eva.conf.gz 0664 root root 217 24958 1521620820
1 f none /usr/share/doc/opensvc/template.node.freenas.conf.gz 0664 root root 222 28178 1521620820
1 f none /usr/share/doc/opensvc/template.node.gcedisks.conf.gz 0664 root root 223 27488 1521620820
1 f none /usr/share/doc/opensvc/template.node.hb.disk.conf.gz 0664 root root 349 43314 1521620820
1 f none /usr/share/doc/opensvc/template.node.hb.multicast.conf.gz 0664 root root 368 45690 1521620820
1 f none /usr/share/doc/opensvc/template.node.hb.relay.conf.gz 0664 root root 357 44347 1521620820
1 f none /usr/share/doc/opensvc/template.node.hb.unicast.conf.gz 0664 root root 383 48584 1521620820
1 f none /usr/share/doc/opensvc/template.node.hds.conf.gz 0664 root root 217 25873 1521620820
1 f none /usr/share/doc/opensvc/template.node.hp3par.conf.gz 0664 root root 223 27089 1521620820
1 f none /usr/share/doc/opensvc/template.node.ibmds.conf.gz 0664 root root 221 26204 1521620820
1 f none /usr/share/doc/opensvc/template.node.ibmsvc.conf.gz 0664 root root 220 26569 1521620820
1 f none /usr/share/doc/opensvc/template.node.listener.conf.gz 0664 root root 358 44106 1521620820
1 f none /usr/share/doc/opensvc/template.node.necism.conf.gz 0664 root root 220 25353 1521620820
1 f none /usr/share/doc/opensvc/template.node.node.conf.gz 0664 root root 2205 18609 1521620820
1 f none /usr/share/doc/opensvc/template.node.nsr.conf.gz 0664 root root 216 26033 1521620820
1 f none /usr/share/doc/opensvc/template.node.packages.conf.gz 0664 root root 229 25946 1521620820
1 f none /usr/share/doc/opensvc/template.node.patches.conf.gz 0664 root root 228 26607 1521620820
1 f none /usr/share/doc/opensvc/template.node.reboot.conf.gz 0664 root root 540 2880 1521620820
1 f none /usr/share/doc/opensvc/template.node.rotate_root_pw.conf.gz 0664 root root 227 28776 1521620820
1 f none /usr/share/doc/opensvc/template.node.stats.conf.gz 0664 root root 327 40293 1521620820
1 f none /usr/share/doc/opensvc/template.node.stats_collection.conf.gz 0664 root root 227 27131 1521620820
1 f none /usr/share/doc/opensvc/template.node.stonith.conf.gz 0664 root root 238 30771 1521620820
1 f none /usr/share/doc/opensvc/template.node.sym.conf.gz 0664 root root 217 25880 1521620820
1 f none /usr/share/doc/opensvc/template.node.syslog.conf.gz 0664 root root 418 52917 1521620820
1 f none /usr/share/doc/opensvc/template.node.sysreport.conf.gz 0664 root root 266 31421 1521620820
1 f none /usr/share/doc/opensvc/template.node.vioserver.conf.gz 0664 root root 221 27679 1521620820
1 f none /usr/share/doc/opensvc/template.node.vnx.conf.gz 0664 root root 220 27022 1521620820
1 f none /usr/share/doc/opensvc/template.node.xtremio.conf.gz 0664 root root 222 27207 1521620820
1 f none /usr/share/doc/opensvc/template.service.DEFAULT.conf.gz 0664 root root 8312 3859 1521620820
1 f none /usr/share/doc/opensvc/template.service.app.conf.gz 0664 root root 3554 57808 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.amazon.conf.gz 0664 root root 3641 7077 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.docker.conf.gz 0664 root root 3761 12308 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.esx.conf.gz 0664 root root 3559 59222 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.hpvm.conf.gz 0664 root root 3462 44776 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.jail.conf.gz 0664 root root 3562 53888 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.kvm.conf.gz 0664 root root 3598 55905 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.ldom.conf.gz 0664 root root 3462 44371 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.lxc.conf.gz 0664 root root 3679 12270 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.lxd.conf.gz 0664 root root 3577 59798 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.openstack.conf.gz 0664 root root 3642 64206 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.ovm.conf.gz 0664 root root 3639 64678 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.srp.conf.gz 0664 root root 3574 53140 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.vbox.conf.gz 0664 root root 3462 40879 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.vcloud.conf.gz 0664 root root 3681 7686 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.vz.conf.gz 0664 root root 3549 53379 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.xen.conf.gz 0664 root root 3598 58732 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.zone.conf.gz 0664 root root 3827 19003 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.amazon.conf.gz 0664 root root 3531 53197 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.disk.conf.gz 0664 root root 3522 59416 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.drbd.conf.gz 0664 root root 3424 44120 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.gce.conf.gz 0664 root root 3854 24945 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.loop.conf.gz 0664 root root 3375 30561 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.lv.conf.gz 0664 root root 3498 55617 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.lvm.conf.gz 0664 root root 3515 50898 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.md.conf.gz 0664 root root 3507 55481 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.rados.conf.gz 0664 root root 3591 56411 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.raw.conf.gz 0664 root root 3719 7315 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.vdisk.conf.gz 0664 root root 3375 43097 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.veritas.conf.gz 0664 root root 3329 34139 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.vg.conf.gz 0664 root root 3515 59359 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.vmdg.conf.gz 0664 root root 3354 45007 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.zpool.conf.gz 0664 root root 3433 42781 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.bfs.conf.gz 0664 root root 3757 34062 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.btrfs.conf.gz 0664 root root 3759 23482 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.conf.gz 0664 root root 3858 26606 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.directory.conf.gz 0664 root root 3731 14402 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.ext.conf.gz 0664 root root 3758 34128 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.ext2.conf.gz 0664 root root 3758 34127 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.ext3.conf.gz 0664 root root 3758 33261 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.ext4.conf.gz 0664 root root 3759 28974 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.f2fs.conf.gz 0664 root root 3758 29571 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.gfs.conf.gz 0664 root root 3757 33996 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.gfs2.conf.gz 0664 root root 3758 33767 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.gpfs.conf.gz 0664 root root 3758 32500 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.hfs.conf.gz 0664 root root 3757 33916 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.hfsplus.conf.gz 0664 root root 3760 25316 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.hpfs.conf.gz 0664 root root 3758 32277 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.jffs.conf.gz 0664 root root 3758 31335 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.jffs2.conf.gz 0664 root root 3760 31955 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.jfs.conf.gz 0664 root root 3758 25917 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.jfs2.conf.gz 0664 root root 3759 25884 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.logfs.conf.gz 0664 root root 3757 25393 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.minix.conf.gz 0664 root root 3759 32599 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.msdos.conf.gz 0664 root root 3759 29043 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.nilfs.conf.gz 0664 root root 3759 28282 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.ntfs.conf.gz 0664 root root 3758 30492 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.ocfs.conf.gz 0664 root root 3758 23419 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.ocfs2.conf.gz 0664 root root 3759 32827 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.qnx4.conf.gz 0664 root root 3759 31488 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.reiserfs.conf.gz 0664 root root 3761 28576 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.reiserfs4.conf.gz 0664 root root 3762 28956 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.tux3.conf.gz 0664 root root 3759 28325 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.ufs.conf.gz 0664 root root 3757 30532 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.ufs2.conf.gz 0664 root root 3758 31109 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.umsdos.conf.gz 0664 root root 3759 30298 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.vfat.conf.gz 0664 root root 3758 32094 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.vxfs.conf.gz 0664 root root 3758 34295 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.xfs.conf.gz 0664 root root 3758 29104 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.xia.conf.gz 0664 root root 3757 34141 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.zfs.conf.gz 0664 root root 3614 2021 1521620820
1 f none /usr/share/doc/opensvc/template.service.ip.amazon.conf.gz 0664 root root 3878 28934 1521620820
1 f none /usr/share/doc/opensvc/template.service.ip.cni.conf.gz 0664 root root 3737 5003 1521620820
1 f none /usr/share/doc/opensvc/template.service.ip.conf.gz 0664 root root 3833 14243 1521620820
1 f none /usr/share/doc/opensvc/template.service.ip.crossbow.conf.gz 0664 root root 3708 13070 1521620820
1 f none /usr/share/doc/opensvc/template.service.ip.docker.conf.gz 0664 root root 3789 21073 1521620820
1 f none /usr/share/doc/opensvc/template.service.ip.gce.conf.gz 0664 root root 3728 5995 1521620820
1 f none /usr/share/doc/opensvc/template.service.share.nfs.conf.gz 0664 root root 2909 40639 1521620820
1 f none /usr/share/doc/opensvc/template.service.subset.conf.gz 0664 root root 1175 20942 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.btrfs.conf.gz 0664 root root 3644 63704 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.btrfssnap.conf.gz 0664 root root 3739 10327 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.dcsckpt.conf.gz 0664 root root 3698 6270 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.dcssnap.conf.gz 0664 root root 3645 62567 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.dds.conf.gz 0664 root root 4046 52027 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.docker.conf.gz 0664 root root 3559 47971 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.evasnap.conf.gz 0664 root root 3710 10857 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.hp3par.conf.gz 0664 root root 3718 6604 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.hp3parsnap.conf.gz 0664 root root 3596 58342 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.ibmdssnap.conf.gz 0664 root root 3725 4251 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.necismsnap.conf.gz 0664 root root 3505 45767 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.netapp.conf.gz 0664 root root 3707 7167 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.rados.conf.gz 0664 root root 3502 44271 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.rsync.conf.gz 0664 root root 4243 8374 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.s3.conf.gz 0664 root root 3934 41594 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.symclone.conf.gz 0664 root root 3796 17981 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.symsnap.conf.gz 0664 root root 3772 7182 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.symsrdfs.conf.gz 0664 root root 3669 65428 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.zfs.conf.gz 0664 root root 3866 26308 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.zfssnap.conf.gz 0664 root root 3782 12288 1521620820
1 f none /usr/share/doc/opensvc/template.service.task.conf.gz 0664 root root 3204 9964 1521620820
1 f none /usr/share/doc/opensvc/template.stonith.callout.conf.gz 0664 root root 1666 10752 1521620820
1 f none /usr/share/doc/opensvc/template.stonith.ilo.conf.gz 0664 root root 1699 21374 1521620820
1 f none /usr/share/doc/opensvc/template.svc.prov.zfs.conf.gz 0664 root root 249 30291 1521620820
1 f none /usr/share/doc/opensvc/template.svc.prov.zpool.conf.gz 0664 root root 172 20316 1521620820
1 f none /usr/share/doc/opensvc/template.sysreport.conf.gz 0664 root root 414 50775 1521620820
1 d none /usr/share/man 0755 root bin
1 d none /usr/share/man/man1 0755 root bin
1 f none /usr/share/man/man1/nodemgr.1.gz 0664 root root 6840 21952 1521620820
1 f none /usr/share/man/man1/svcmgr.1.gz 0664 root root 10034 21933 1521620820
1 f none /usr/share/man/man1/svcmon.1.gz 0664 root root 959 55183 1521620820
1 d none /usr/share/opensvc 0755 root root
1 d none /usr/share/opensvc/bin 0755 root root
1 d none /usr/share/opensvc/bin/init 0755 root root
1 f none /usr/share/opensvc/bin/init/darwin.com.opensvc.svcmgr.plist 0664 root root 597 51671 1521620820
1 f none /usr/share/opensvc/bin/init/opensvc.agent.xml 0664 root root 2097 49739 1521620820
1 f none /usr/share/opensvc/bin/init/opensvc.init.AIX 0775 root root 459 36992 1521620820
1 f none /usr/share/opensvc/bin/init/opensvc.init.Darwin 0775 root root 830 1316 1521620820
1 f none /usr/share/opensvc/bin/init/opensvc.init.FreeBSD 0775 root root 572 44763 1521620820
1 f none /usr/share/opensvc/bin/init/opensvc.init.OSF1 0775 root root 484 38126 1521620820
1 f none /usr/share/opensvc/bin/init/opensvc.init.SunOS 0775 root root 665 53113 1521620820
1 f none /usr/share/opensvc/bin/init/opensvc.init.debian 0775 root root 723 54824 1521620820
1 f none /usr/share/opensvc/bin/init/opensvc.init.hpux 0775 root root 962 9971 1521620820
1 f none /usr/share/opensvc/bin/init/opensvc.init.redhat 0775 root root 841 2538 1521620820
1 f none /usr/share/opensvc/bin/init/opensvc.init.suse 0775 root root 727 55057 1521620820
1 f none /usr/share/opensvc/bin/init/systemd.opensvc-agent.service 0664 root root 513 48250 1521620820
1 f none /usr/share/opensvc/bin/init/systemd.opensvc-services.service 0664 root root 354 33229 1521620820
1 s none /usr/share/opensvc/bin/nodemgr=opensvc
1 f none /usr/share/opensvc/bin/opensvc 0775 root root 1126 22334 1521620820
1 f none /usr/share/opensvc/bin/postinstall 0775 root root 41509 25465 1521620820
1 f none /usr/share/opensvc/bin/preinstall 0775 root root 1211 34464 1521620820
1 f none /usr/share/opensvc/bin/preuninstall 0775 root root 403 31121 1521620820
1 s none /usr/share/opensvc/bin/svcmgr=opensvc
1 s none /usr/share/opensvc/bin/svcmon=opensvc
1 d none /usr/share/opensvc/lib 0755 root root
1 f none /usr/share/opensvc/lib/checkAdvfsUsageOSF1.py 0664 root root 887 61564 1521620820
1 f none /usr/share/opensvc/lib/checkBtrfsDevStatsLinux.py 0664 root root 2505 45928 1521620820
1 f none /usr/share/opensvc/lib/checkEthHP-UX.py 0664 root root 2795 43220 1521620820
1 f none /usr/share/opensvc/lib/checkEthLinux.py 0664 root root 2848 53741 1521620820
1 f none /usr/share/opensvc/lib/checkEthSunOS.py 0664 root root 10182 13886 1521620820
1 f none /usr/share/opensvc/lib/checkFmFmadm.py 0664 root root 1049 11080 1521620820
1 s none /usr/share/opensvc/lib/checkFmFmadmSunOS.py=checkFmFmadm.py
1 f none /usr/share/opensvc/lib/checkFmOpenManage.py 0664 root root 1640 50564 1521620820
1 s none /usr/share/opensvc/lib/checkFmOpenManageLinux.py=checkFmOpenManage.py
1 f none /usr/share/opensvc/lib/checkFsInodeAIX.py 0664 root root 995 65387 1521620820
1 f none /usr/share/opensvc/lib/checkFsInodeDarwin.py 0664 root root 959 64537 1521620820
1 f none /usr/share/opensvc/lib/checkFsInodeHP-UX.py 0664 root root 1370 21841 1521620820
1 f none /usr/share/opensvc/lib/checkFsInodeLinux.py 0664 root root 1780 53406 1521620820
1 f none /usr/share/opensvc/lib/checkFsInodeSunOS.py 0664 root root 1091 6327 1521620820
1 f none /usr/share/opensvc/lib/checkFsUsageAIX.py 0664 root root 995 65370 1521620820
1 f none /usr/share/opensvc/lib/checkFsUsageDarwin.py 0664 root root 1468 34105 1521620820
1 f none /usr/share/opensvc/lib/checkFsUsageFreeBSD.py 0664 root root 1468 34105 1521620820
1 f none /usr/share/opensvc/lib/checkFsUsageHP-UX.py 0664 root root 1468 34105 1521620820
1 f none /usr/share/opensvc/lib/checkFsUsageLinux.py 0664 root root 2062 3189 1521620820
1 f none /usr/share/opensvc/lib/checkFsUsageSunOS.py 0664 root root 1488 28806 1521620820
1 f none /usr/share/opensvc/lib/checkFsUsageWindows.py 0664 root root 944 377 1521620820
1 f none /usr/share/opensvc/lib/checkLagHP-UX.py 0664 root root 1895 60326 1521620820
1 f none /usr/share/opensvc/lib/checkLagLinux.py 0664 root root 4063 20259 1521620820
1 f none /usr/share/opensvc/lib/checkLagSunOS.py 0664 root root 6841 11782 1521620820
1 f none /usr/share/opensvc/lib/checkLagWindows.py 0664 root root 763 53693 1521620820
1 f none /usr/share/opensvc/lib/checkMceLinux.py 0664 root root 1753 54530 1521620820
1 f none /usr/share/opensvc/lib/checkMpathAIX.py 0664 root root 2272 17452 1521620820
1 f none /usr/share/opensvc/lib/checkMpathHP-UX.py 0664 root root 2335 23985 1521620820
1 f none /usr/share/opensvc/lib/checkMpathLinux.py 0664 root root 4337 15881 1521620820
1 f none /usr/share/opensvc/lib/checkMpathOSF1.py 0664 root root 867 60569 1521620820
1 f none /usr/share/opensvc/lib/checkMpathPowerpath.py 0664 root root 2828 45262 1521620820
1 s none /usr/share/opensvc/lib/checkMpathPowerpathAIX.py=checkMpathPowerpath.py
1 s none /usr/share/opensvc/lib/checkMpathPowerpathHP-UX.py=checkMpathPowerpath.py
1 s none /usr/share/opensvc/lib/checkMpathPowerpathLinux.py=checkMpathPowerpath.py
1 s none /usr/share/opensvc/lib/checkMpathPowerpathSunOS.py=checkMpathPowerpath.py
1 f none /usr/share/opensvc/lib/checkMpathSunOS.py 0664 root root 2608 34126 1521620820
1 f none /usr/share/opensvc/lib/checkMpathWindows.py 0664 root root 1440 33261 1521620820
1 f none /usr/share/opensvc/lib/checkNumaLinux.py 0664 root root 1419 27818 1521620820
1 f none /usr/share/opensvc/lib/checkRaidMegaRaid.py 0664 root root 4594 39091 1521620820
1 s none /usr/share/opensvc/lib/checkRaidMegaRaidLinux.py=checkRaidMegaRaid.py
1 s none /usr/share/opensvc/lib/checkRaidMegaRaidSunOS.py=checkRaidMegaRaid.py
1 f none /usr/share/opensvc/lib/checkRaidSas2.py 0664 root root 3777 57148 1521620820
1 s none /usr/share/opensvc/lib/checkRaidSas2Linux.py=checkRaidSas2.py
1 s none /usr/share/opensvc/lib/checkRaidSas2SunOS.py=checkRaidSas2.py
1 f none /usr/share/opensvc/lib/checkRaidSmartArray.py 0664 root root 3095 18409 1521620820
1 s none /usr/share/opensvc/lib/checkRaidSmartArrayLinux.py=checkRaidSmartArray.py
1 s none /usr/share/opensvc/lib/checkRaidSmartArraySunOS.py=checkRaidSmartArray.py
1 f none /usr/share/opensvc/lib/checkRaidSmartArrayWindows.py 0664 root root 520 39436 1521620820
1 f none /usr/share/opensvc/lib/checkSync.py 0664 root root 1248 22180 1521620820
1 f none /usr/share/opensvc/lib/checkVgUsageAIX.py 0664 root root 1255 18069 1521620820
1 f none /usr/share/opensvc/lib/checkVgUsageHP-UX.py 0664 root root 1772 58287 1521620820
1 f none /usr/share/opensvc/lib/checkVgUsageLinux.py 0664 root root 1210 16640 1521620820
1 s none /usr/share/opensvc/lib/checkZfsUsageFreeBSD.py=checkZfsUsageSunOS.py
1 s none /usr/share/opensvc/lib/checkZfsUsageLinux.py=checkZfsUsageSunOS.py
1 f none /usr/share/opensvc/lib/checkZfsUsageSunOS.py 0664 root root 3085 9389 1521620820
1 s none /usr/share/opensvc/lib/checkZpoolFreeBSD.py=checkZpoolSunOS.py
1 s none /usr/share/opensvc/lib/checkZpoolLinux.py=checkZpoolSunOS.py
1 f none /usr/share/opensvc/lib/checkZpoolSunOS.py 0664 root root 2197 12700 1521620820
1 f none /usr/share/opensvc/lib/checks.py 0664 root root 5247 37281 1521620820
1 f none /usr/share/opensvc/lib/collector.py 0664 root root 11257 60807 1521620820
1 f none /usr/share/opensvc/lib/comm.py 0664 root root 20721 43263 1521620820
1 f none /usr/share/opensvc/lib/compliance.py 0664 root root 28760 39752 1521620820
1 f none /usr/share/opensvc/lib/converters.py 0664 root root 6730 22756 1521620820
1 f none /usr/share/opensvc/lib/extconfig.py 0664 root root 43302 35491 1521620820
1 f none /usr/share/opensvc/lib/forest.py 0664 root root 13364 9669 1521620820
1 f none /usr/share/opensvc/lib/freezer.py 0664 root root 2171 31605 1521620820
1 f none /usr/share/opensvc/lib/hb.py 0664 root root 11664 22564 1521620820
1 f none /usr/share/opensvc/lib/hb_disk.py 0664 root root 11638 48348 1521620820
1 f none /usr/share/opensvc/lib/hb_mcast.py 0664 root root 6839 39478 1521620820
1 f none /usr/share/opensvc/lib/hb_relay.py 0664 root root 7016 48013 1521620820
1 f none /usr/share/opensvc/lib/hb_ucast.py 0664 root root 7668 33064 1521620820
1 f none /usr/share/opensvc/lib/hostidAIX.py 0664 root root 285 21867 1521620820
1 f none /usr/share/opensvc/lib/hostidDarwin.py 0664 root root 66 5626 1521620820
1 s none /usr/share/opensvc/lib/hostidFreeBSD.py=hostidLinux.py
1 f none /usr/share/opensvc/lib/hostidHP-UX.py 0664 root root 381 29514 1521620820
1 f none /usr/share/opensvc/lib/hostidLinux.py 0664 root root 66 5626 1521620820
1 f none /usr/share/opensvc/lib/hostidOSF1.py 0664 root root 66 5626 1521620820
1 f none /usr/share/opensvc/lib/hostidSunOS.py 0664 root root 66 5626 1521620820
1 f none /usr/share/opensvc/lib/hostidWindows.py 0664 root root 66 5626 1521620820
1 f none /usr/share/opensvc/lib/ipaddress.py 0664 root root 79904 11072 1521620820
1 d none /usr/share/opensvc/lib/json_delta 0755 root root
1 f none /usr/share/opensvc/lib/json_delta/__init__.py 0664 root root 9208 47046 1521620820
1 f none /usr/share/opensvc/lib/json_delta/_diff.py 0664 root root 16578 38614 1521620820
1 f none /usr/share/opensvc/lib/json_delta/_patch.py 0664 root root 2442 60988 1521620820
1 f none /usr/share/opensvc/lib/json_delta/_udiff.py 0664 root root 18888 45438 1521620820
1 f none /usr/share/opensvc/lib/json_delta/_upatch.py 0664 root root 7922 20585 1521620820
1 f none /usr/share/opensvc/lib/json_delta/_util.py 0664 root root 21132 8738 1521620820
1 f none /usr/share/opensvc/lib/keywords.py 0664 root root 16215 8479 1521620820
1 f none /usr/share/opensvc/lib/lock.py 0664 root root 6493 26880 1521620820
1 f none /usr/share/opensvc/lib/node.py 0664 root root 150104 7563 1521620820
1 f none /usr/share/opensvc/lib/nodeDarwin.py 0664 root root 244 17954 1521620820
1 f none /usr/share/opensvc/lib/nodeHP-UX.py 0664 root root 252 17990 1521620820
1 f none /usr/share/opensvc/lib/nodeLinux.py 0664 root root 1121 15189 1521620820
1 f none /usr/share/opensvc/lib/nodeSunOS.py 0664 root root 1560 46090 1521620820
1 f none /usr/share/opensvc/lib/nodedict.py 0664 root root 44958 55241 1521620820
1 f none /usr/share/opensvc/lib/nodemgr.py 0664 root root 2565 6429 1521620820
1 f none /usr/share/opensvc/lib/nodemgr_parser.py 0664 root root 36829 19889 1521620820
1 f none /usr/share/opensvc/lib/osvcd.py 0664 root root 13216 53436 1521620820
1 f none /usr/share/opensvc/lib/osvcd_collector.py 0664 root root 6822 56747 1521620820
1 f none /usr/share/opensvc/lib/osvcd_dns.py 0664 root root 14782 34654 1521620820
1 f none /usr/share/opensvc/lib/osvcd_lsnr.py 0664 root root 26959 63995 1521620820
1 f none /usr/share/opensvc/lib/osvcd_mon.py 0664 root root 98380 15497 1521620820
1 f none /usr/share/opensvc/lib/osvcd_scheduler.py 0664 root root 6086 39507 1521620820
1 f none /usr/share/opensvc/lib/osvcd_shared.py 0664 root root 31767 59703 1521620820
1 f none /usr/share/opensvc/lib/osvcd_winservice.py 0664 root root 2221 46683 1521620820
1 f none /usr/share/opensvc/lib/provApp.py 0664 root root 234 17832 1521620820
1 f none /usr/share/opensvc/lib/provContainerAmazon.py 0664 root root 1314 39736 1521620820
1 f none /usr/share/opensvc/lib/provContainerDocker.py 0664 root root 267 21179 1521620820
1 f none /usr/share/opensvc/lib/provContainerLxd.py 0664 root root 1136 21856 1521620820
1 f none /usr/share/opensvc/lib/provContainerOpenstack.py 0664 root root 555 46693 1521620820
1 f none /usr/share/opensvc/lib/provContainerVcloud.py 0664 root root 488 40948 1521620820
1 f none /usr/share/opensvc/lib/provContainerZone.py 0664 root root 14571 28903 1521620820
1 f none /usr/share/opensvc/lib/provDiskAmazon.py 0664 root root 1768 3253 1521620820
1 f none /usr/share/opensvc/lib/provDiskDisk.py 0664 root root 2132 31823 1521620820
1 f none /usr/share/opensvc/lib/provDiskGce.py 0664 root root 2469 51225 1521620820
1 f none /usr/share/opensvc/lib/provDiskLoopDarwin.py 0664 root root 1445 42699 1521620820
1 f none /usr/share/opensvc/lib/provDiskLoopLinux.py 0664 root root 1445 42699 1521620820
1 f none /usr/share/opensvc/lib/provDiskLvHP-UX.py 0664 root root 1361 39551 1521620820
1 f none /usr/share/opensvc/lib/provDiskLvLinux.py 0664 root root 6372 64369 1521620820
1 f none /usr/share/opensvc/lib/provDiskMdLinux.py 0664 root root 3430 55846 1521620820
1 f none /usr/share/opensvc/lib/provDiskRadosLinux.py 0664 root root 1076 18009 1521620820
1 f none /usr/share/opensvc/lib/provDiskRaw.py 0664 root root 129 11392 1521620820
1 f none /usr/share/opensvc/lib/provDiskVgHP-UX.py 0664 root root 1774 61052 1521620820
1 f none /usr/share/opensvc/lib/provDiskVgLinux.py 0664 root root 3708 4132 1521620820
1 f none /usr/share/opensvc/lib/provDiskZpool.py 0664 root root 934 5046 1521620820
1 f none /usr/share/opensvc/lib/provFs.py 0664 root root 5446 5978 1521620820
1 f none /usr/share/opensvc/lib/provFsBtrfs.py 0664 root root 3811 19292 1521620820
1 f none /usr/share/opensvc/lib/provFsDir.py 0664 root root 263 20875 1521620820
1 f none /usr/share/opensvc/lib/provFsDocker.py 0664 root root 1186 26817 1521620820
1 f none /usr/share/opensvc/lib/provFsExt2.py 0664 root root 106 7982 1521620820
1 f none /usr/share/opensvc/lib/provFsExt3.py 0664 root root 106 7983 1521620820
1 f none /usr/share/opensvc/lib/provFsExt4.py 0664 root root 106 7984 1521620820
1 f none /usr/share/opensvc/lib/provFsHfs.py 0664 root root 2581 44215 1521620820
1 f none /usr/share/opensvc/lib/provFsVxfs.py 0664 root root 130 9689 1521620820
1 f none /usr/share/opensvc/lib/provFsXfs.py 0664 root root 108 8216 1521620820
1 f none /usr/share/opensvc/lib/provFsZfs.py 0664 root root 2066 30681 1521620820
1 f none /usr/share/opensvc/lib/provIp.py 0664 root root 1029 12654 1521620820
1 f none /usr/share/opensvc/lib/provIpAmazon.py 0664 root root 3290 54620 1521620820
1 f none /usr/share/opensvc/lib/provKvm.py 0664 root root 4517 64748 1521620820
1 f none /usr/share/opensvc/lib/provLxc.py 0664 root root 8603 10370 1521620820
1 f none /usr/share/opensvc/lib/provShare.py 0664 root root 189 15547 1521620820
1 f none /usr/share/opensvc/lib/provSrp.py 0664 root root 2940 9313 1521620820
1 f none /usr/share/opensvc/lib/provisioning.py 0664 root root 937 5426 1521620820
1 d none /usr/share/opensvc/lib/pyaes 0755 root root
1 f none /usr/share/opensvc/lib/pyaes/__init__.py 0664 root root 2087 40499 1521620820
1 f none /usr/share/opensvc/lib/pyaes/aes.py 0664 root root 60310 16766 1521620820
1 f none /usr/share/opensvc/lib/pyaes/blockfeeder.py 0664 root root 8119 11752 1521620820
1 f none /usr/share/opensvc/lib/pyaes/util.py 0664 root root 2050 33260 1521620820
1 f none /usr/share/opensvc/lib/rcAddService.py 0664 root root 95 7538 1521620820
1 f none /usr/share/opensvc/lib/rcAdvfs.py 0664 root root 6389 37737 1521620820
1 f none /usr/share/opensvc/lib/rcAmazon.py 0664 root root 1472 47966 1521620820
1 f none /usr/share/opensvc/lib/rcAsset.py 0664 root root 21179 71 1521620820
1 f none /usr/share/opensvc/lib/rcAssetAIX.py 0664 root root 3130 22136 1521620820
1 f none /usr/share/opensvc/lib/rcAssetDarwin.py 0664 root root 3178 32595 1521620820
1 f none /usr/share/opensvc/lib/rcAssetFreeBSD.py 0664 root root 2096 15927 1521620820
1 f none /usr/share/opensvc/lib/rcAssetHP-UX.py 0664 root root 8452 54560 1521620820
1 f none /usr/share/opensvc/lib/rcAssetLinux.py 0664 root root 21112 36970 1521620820
1 f none /usr/share/opensvc/lib/rcAssetOSF1.py 0664 root root 10914 31165 1521620820
1 f none /usr/share/opensvc/lib/rcAssetSunOS.py 0664 root root 8039 27545 1521620820
1 f none /usr/share/opensvc/lib/rcAssetWindows.py 0664 root root 8832 23619 1521620820
1 f none /usr/share/opensvc/lib/rcBrocade.py 0664 root root 4263 49336 1521620820
1 f none /usr/share/opensvc/lib/rcBtrfs.py 0664 root root 13541 53538 1521620820
1 f none /usr/share/opensvc/lib/rcCentera.py 0664 root root 3078 35176 1521620820
1 f none /usr/share/opensvc/lib/rcCloud.py 0664 root root 444 33680 1521620820
1 f none /usr/share/opensvc/lib/rcCloudAmazon.py 0664 root root 1283 40589 1521620820
1 f none /usr/share/opensvc/lib/rcCloudGandi.py 0664 root root 1352 38858 1521620820
1 f none /usr/share/opensvc/lib/rcCloudOpenstack.py 0664 root root 2152 37985 1521620820
1 f none /usr/share/opensvc/lib/rcCloudVcloud.py 0664 root root 1900 16684 1521620820
1 f none /usr/share/opensvc/lib/rcCollectorCli.py 0775 root root 97155 17295 1521620820
1 f none /usr/share/opensvc/lib/rcColor.py 0664 root root 9587 35561 1521620820
1 f none /usr/share/opensvc/lib/rcConfigParser.py 0664 root root 2075 19030 1521620820
1 f none /usr/share/opensvc/lib/rcDcs.py 0664 root root 9489 24840 1521620820
1 f none /usr/share/opensvc/lib/rcDevTree.py 0664 root root 13185 53937 1521620820
1 f none /usr/share/opensvc/lib/rcDevTreeAIX.py 0664 root root 2040 16817 1521620820
1 f none /usr/share/opensvc/lib/rcDevTreeHP-UX.py 0664 root root 6121 31912 1521620820
1 f none /usr/share/opensvc/lib/rcDevTreeLinux.py 0664 root root 20336 33888 1521620820
1 f none /usr/share/opensvc/lib/rcDevTreeOSF1.py 0664 root root 3072 11174 1521620820
1 f none /usr/share/opensvc/lib/rcDevTreeSunOS.py 0664 root root 10369 6898 1521620820
1 f none /usr/share/opensvc/lib/rcDevTreeVeritas.py 0664 root root 8181 26525 1521620820
1 f none /usr/share/opensvc/lib/rcDevTreeWindows.py 0664 root root 619 47948 1521620820
1 f none /usr/share/opensvc/lib/rcDiskInfo.py 0664 root root 669 48059 1521620820
1 f none /usr/share/opensvc/lib/rcDiskInfoAIX.py 0664 root root 2098 15715 1521620820
1 f none /usr/share/opensvc/lib/rcDiskInfoDarwin.py 0664 root root 65 5711 1521620820
1 f none /usr/share/opensvc/lib/rcDiskInfoFreeBSD.py 0664 root root 538 41094 1521620820
1 f none /usr/share/opensvc/lib/rcDiskInfoHP-UX.py 0664 root root 6697 7048 1521620820
1 f none /usr/share/opensvc/lib/rcDiskInfoLinux.py 0664 root root 12649 35348 1521620820
1 f none /usr/share/opensvc/lib/rcDiskInfoOSF1.py 0664 root root 2866 64303 1521620820
1 f none /usr/share/opensvc/lib/rcDiskInfoSunOS.py 0664 root root 5106 12482 1521620820
1 f none /usr/share/opensvc/lib/rcDiskInfoWindows.py 0664 root root 3598 39919 1521620820
1 f none /usr/share/opensvc/lib/rcDocker.py 0664 root root 33031 47112 1521620820
1 f none /usr/share/opensvc/lib/rcEmcVnx.py 0664 root root 4817 26908 1521620820
1 f none /usr/share/opensvc/lib/rcEthtool.py 0664 root root 1737 56779 1521620820
1 f none /usr/share/opensvc/lib/rcEva.py 0664 root root 4523 5619 1521620820
1 f none /usr/share/opensvc/lib/rcExceptions.py 0664 root root 3505 20406 1521620820
1 f none /usr/share/opensvc/lib/rcFreenas.py 0664 root root 32016 54639 1521620820
1 f none /usr/share/opensvc/lib/rcGce.py 0664 root root 777 58025 1521620820
1 f none /usr/share/opensvc/lib/rcGceDisks.py 0664 root root 1254 30197 1521620820
1 f none /usr/share/opensvc/lib/rcGlobalEnv.py 0664 root root 7471 59385 1521620820
1 f none /usr/share/opensvc/lib/rcHds.py 0664 root root 25605 54056 1521620820
1 f none /usr/share/opensvc/lib/rcHp3par.py 0664 root root 15029 32282 1521620820
1 f none /usr/share/opensvc/lib/rcIbmDs.py 0664 root root 3458 59022 1521620820
1 f none /usr/share/opensvc/lib/rcIbmSvc.py 0664 root root 3014 26759 1521620820
1 f none /usr/share/opensvc/lib/rcIfconfig.py 0664 root root 4636 4111 1521620820
1 f none /usr/share/opensvc/lib/rcIfconfigAIX.py 0664 root root 2769 51138 1521620820
1 f none /usr/share/opensvc/lib/rcIfconfigDarwin.py 0664 root root 2932 59705 1521620820
1 f none /usr/share/opensvc/lib/rcIfconfigFreeBSD.py 0664 root root 1953 59987 1521620820
1 f none /usr/share/opensvc/lib/rcIfconfigHP-UX.py 0664 root root 4987 55926 1521620820
1 f none /usr/share/opensvc/lib/rcIfconfigLinux.py 0664 root root 9353 59146 1521620820
1 f none /usr/share/opensvc/lib/rcIfconfigOSF1.py 0664 root root 3138 7251 1521620820
1 f none /usr/share/opensvc/lib/rcIfconfigSunOS.py 0664 root root 4834 54113 1521620820
1 f none /usr/share/opensvc/lib/rcIfconfigWindows.py 0664 root root 1334 30901 1521620820
1 f none /usr/share/opensvc/lib/rcLogger.py 0664 root root 7438 18009 1521620820
1 f none /usr/share/opensvc/lib/rcLoopDarwin.py 0664 root root 854 64731 1521620820
1 f none /usr/share/opensvc/lib/rcLoopLinux.py 0664 root root 1549 50509 1521620820
1 f none /usr/share/opensvc/lib/rcLvmAIX.py 0664 root root 7970 44000 1521620820
1 f none /usr/share/opensvc/lib/rcMd5.py 0664 root root 9367 14807 1521620820
1 f none /usr/share/opensvc/lib/rcMounts.py 0664 root root 2505 50756 1521620820
1 f none /usr/share/opensvc/lib/rcMountsAIX.py 0664 root root 3445 24185 1521620820
1 f none /usr/share/opensvc/lib/rcMountsDarwin.py 0664 root root 1341 28103 1521620820
1 f none /usr/share/opensvc/lib/rcMountsFreeBSD.py 0664 root root 1341 28103 1521620820
1 f none /usr/share/opensvc/lib/rcMountsHP-UX.py 0664 root root 1157 18837 1521620820
1 f none /usr/share/opensvc/lib/rcMountsLinux.py 0664 root root 1221 25481 1521620820
1 f none /usr/share/opensvc/lib/rcMountsOSF1.py 0664 root root 936 2616 1521620820
1 f none /usr/share/opensvc/lib/rcMountsSunOS.py 0664 root root 1141 16786 1521620820
1 f none /usr/share/opensvc/lib/rcMountsWindows.py 0664 root root 1733 3380 1521620820
1 f none /usr/share/opensvc/lib/rcNecIsm.py 0664 root root 11160 57005 1521620820
1 f none /usr/share/opensvc/lib/rcNetapp.py 0664 root root 2869 13635 1521620820
1 f none /usr/share/opensvc/lib/rcNexenta.py 0664 root root 11108 17875 1521620820
1 f none /usr/share/opensvc/lib/rcNode.py 0664 root root 1003 13252 1521620820
1 f none /usr/share/opensvc/lib/rcNsr.py 0664 root root 1179 17717 1521620820
1 f none /usr/share/opensvc/lib/rcOptParser.py 0664 root root 13908 7682 1521620820
1 f none /usr/share/opensvc/lib/rcOs.py 0664 root root 128 9832 1521620820
1 f none /usr/share/opensvc/lib/rcOsHP-UX.py 0664 root root 122 10053 1521620820
1 f none /usr/share/opensvc/lib/rcOsLinux.py 0664 root root 260 19577 1521620820
1 f none /usr/share/opensvc/lib/rcOsSunOS.py 0664 root root 177 13842 1521620820
1 f none /usr/share/opensvc/lib/rcOvm.py 0664 root root 3600 1558 1521620820
1 f none /usr/share/opensvc/lib/rcPasswdAIX.py 0664 root root 347 27171 1521620820
1 f none /usr/share/opensvc/lib/rcPasswdFreeBSD.py 0664 root root 331 25913 1521620820
1 f none /usr/share/opensvc/lib/rcPasswdLinux.py 0664 root root 353 27529 1521620820
1 f none /usr/share/opensvc/lib/rcPasswdSunOS.py 0664 root root 1970 12852 1521620820
1 f none /usr/share/opensvc/lib/rcPgLinux.py 0664 root root 12055 63594 1521620820
1 f none /usr/share/opensvc/lib/rcPkgAIX.py 0664 root root 508 37629 1521620820
1 f none /usr/share/opensvc/lib/rcPkgDarwin.py 0664 root root 993 10719 1521620820
1 f none /usr/share/opensvc/lib/rcPkgFreeBSD.py 0664 root root 1019 10121 1521620820
1 f none /usr/share/opensvc/lib/rcPkgHP-UX.py 0664 root root 824 59934 1521620820
1 f none /usr/share/opensvc/lib/rcPkgLinux.py 0664 root root 1690 59442 1521620820
1 f none /usr/share/opensvc/lib/rcPkgOSF1.py 0664 root root 1310 29504 1521620820
1 f none /usr/share/opensvc/lib/rcPkgSunOS.py 0664 root root 2818 3579 1521620820
1 f none /usr/share/opensvc/lib/rcPkgWindows.py 0664 root root 4532 13645 1521620820
1 f none /usr/share/opensvc/lib/rcPrintTable.py 0664 root root 4368 46202 1521620820
1 f none /usr/share/opensvc/lib/rcScheduler.py 0664 root root 45441 27303 1521620820
1 f none /usr/share/opensvc/lib/rcStats.py 0664 root root 3416 58275 1521620820
1 f none /usr/share/opensvc/lib/rcStatsAIX.py 0664 root root 4086 34947 1521620820
1 f none /usr/share/opensvc/lib/rcStatsCollectDarwin.py 0664 root root 2733 47042 1521620820
1 f none /usr/share/opensvc/lib/rcStatsCollectHP-UX.py 0664 root root 3564 44830 1521620820
1 f none /usr/share/opensvc/lib/rcStatsCollectLinux.py 0664 root root 5136 20462 1521620820
1 f none /usr/share/opensvc/lib/rcStatsCollectSunOS.py 0664 root root 6090 7875 1521620820
1 f none /usr/share/opensvc/lib/rcStatsDarwin.py 0664 root root 4704 38226 1521620820
1 f none /usr/share/opensvc/lib/rcStatsFreeBSD.py 0664 root root 4429 15820 1521620820
1 f none /usr/share/opensvc/lib/rcStatsHP-UX.py 0664 root root 6813 63717 1521620820
1 f none /usr/share/opensvc/lib/rcStatsLinux.py 0664 root root 13765 33604 1521620820
1 f none /usr/share/opensvc/lib/rcStatsSunOS.py 0664 root root 7372 5685 1521620820
1 f none /usr/share/opensvc/lib/rcStatus.py 0664 root root 6073 62071 1521620820
1 f none /usr/share/opensvc/lib/rcSymmetrix.py 0664 root root 40733 62639 1521620820
1 f none /usr/share/opensvc/lib/rcSysReport.py 0664 root root 13640 59632 1521620820
1 f none /usr/share/opensvc/lib/rcSysReportAIX.py 0664 root root 152 13334 1521620820
1 f none /usr/share/opensvc/lib/rcSysReportDarwin.py 0664 root root 152 13334 1521620820
1 f none /usr/share/opensvc/lib/rcSysReportFreeBSD.py 0664 root root 152 13334 1521620820
1 f none /usr/share/opensvc/lib/rcSysReportHP-UX.py 0664 root root 152 13334 1521620820
1 f none /usr/share/opensvc/lib/rcSysReportLinux.py 0664 root root 152 13334 1521620820
1 f none /usr/share/opensvc/lib/rcSysReportOSF1.py 0664 root root 152 13334 1521620820
1 f none /usr/share/opensvc/lib/rcSysReportSunOS.py 0664 root root 152 13334 1521620820
1 f none /usr/share/opensvc/lib/rcUpdatePkgAIX.py 0664 root root 223 17362 1521620820
1 f none /usr/share/opensvc/lib/rcUpdatePkgDarwin.py 0664 root root 448 35295 1521620820
1 f none /usr/share/opensvc/lib/rcUpdatePkgHP-UX.py 0664 root root 264 21363 1521620820
1 f none /usr/share/opensvc/lib/rcUpdatePkgLinux.py 0664 root root 578 45763 1521620820
1 f none /usr/share/opensvc/lib/rcUpdatePkgOSF1.py 0664 root root 685 52234 1521620820
1 f none /usr/share/opensvc/lib/rcUpdatePkgSunOS.py 0664 root root 1626 58834 1521620820
1 f none /usr/share/opensvc/lib/rcUpdatePkgWindows.py 0664 root root 178 13967 1521620820
1 f none /usr/share/opensvc/lib/rcUtilities.py 0664 root root 30779 33797 1521620820
1 f none /usr/share/opensvc/lib/rcUtilitiesAIX.py 0664 root root 379 28131 1521620820
1 f none /usr/share/opensvc/lib/rcUtilitiesDarwin.py 0664 root root 429 30256 1521620820
1 f none /usr/share/opensvc/lib/rcUtilitiesFreeBSD.py 0664 root root 346 25966 1521620820
1 f none /usr/share/opensvc/lib/rcUtilitiesHP-UX.py 0664 root root 315 23090 1521620820
1 f none /usr/share/opensvc/lib/rcUtilitiesLinux.py 0664 root root 7407 33255 1521620820
1 f none /usr/share/opensvc/lib/rcUtilitiesOSF1.py 0664 root root 315 23086 1521620820
1 f none /usr/share/opensvc/lib/rcUtilitiesSunOS.py 0664 root root 1325 29989 1521620820
1 f none /usr/share/opensvc/lib/rcUtilitiesWindows.py 0664 root root 826 150 1521620820
1 f none /usr/share/opensvc/lib/rcVioServer.py 0664 root root 3310 51262 1521620820
1 f none /usr/share/opensvc/lib/rcWakeOnLan.py 0664 root root 1425 40136 1521620820
1 f none /usr/share/opensvc/lib/rcXtremio.py 0664 root root 26731 58450 1521620820
1 f none /usr/share/opensvc/lib/rcZfs.py 0664 root root 6598 21656 1521620820
1 f none /usr/share/opensvc/lib/rcZone.py 0664 root root 3215 47715 1521620820
1 f none /usr/share/opensvc/lib/resApp.py 0664 root root 16349 22896 1521620820
1 f none /usr/share/opensvc/lib/resAppVm.py 0664 root root 3241 42280 1521620820
1 f none /usr/share/opensvc/lib/resContainer.py 0664 root root 8332 20177 1521620820
1 f none /usr/share/opensvc/lib/resContainerAmazon.py 0664 root root 9682 32993 1521620820
1 f none /usr/share/opensvc/lib/resContainerDocker.py 0664 root root 21963 50349 1521620820
1 f none /usr/share/opensvc/lib/resContainerEsx.py 0664 root root 3551 50279 1521620820
1 f none /usr/share/opensvc/lib/resContainerHpVm.py 0664 root root 4044 27602 1521620820
1 f none /usr/share/opensvc/lib/resContainerJail.py 0664 root root 3361 37986 1521620820
1 f none /usr/share/opensvc/lib/resContainerKvm.py 0664 root root 5650 13677 1521620820
1 f none /usr/share/opensvc/lib/resContainerLdom.py 0664 root root 4124 22970 1521620820
1 f none /usr/share/opensvc/lib/resContainerLxc.py 0664 root root 14804 64835 1521620820
1 f none /usr/share/opensvc/lib/resContainerLxd.py 0664 root root 8065 5666 1521620820
1 f none /usr/share/opensvc/lib/resContainerOpenstack.py 0664 root root 10775 50515 1521620820
1 f none /usr/share/opensvc/lib/resContainerOvm.py 0664 root root 5002 23092 1521620820
1 f none /usr/share/opensvc/lib/resContainerSrp.py 0664 root root 7941 22296 1521620820
1 f none /usr/share/opensvc/lib/resContainerVbox.py 0664 root root 4052 23361 1521620820
1 f none /usr/share/opensvc/lib/resContainerVcloud.py 0664 root root 8896 59237 1521620820
1 f none /usr/share/opensvc/lib/resContainerVz.py 0664 root root 4828 5126 1521620820
1 f none /usr/share/opensvc/lib/resContainerXen.py 0664 root root 3454 49898 1521620820
1 f none /usr/share/opensvc/lib/resContainerZone.py 0664 root root 17334 64680 1521620820
1 f none /usr/share/opensvc/lib/resDisk.py 0664 root root 1760 507 1521620820
1 f none /usr/share/opensvc/lib/resDiskAmazon.py 0664 root root 7662 40696 1521620820
1 f none /usr/share/opensvc/lib/resDiskDisk.py 0664 root root 3964 16011 1521620820
1 f none /usr/share/opensvc/lib/resDiskDiskLinux.py 0664 root root 1424 44806 1521620820
1 f none /usr/share/opensvc/lib/resDiskDrbd.py 0664 root root 6379 61839 1521620820
1 f none /usr/share/opensvc/lib/resDiskGandi.py 0664 root root 6117 39737 1521620820
1 f none /usr/share/opensvc/lib/resDiskGce.py 0664 root root 5695 25739 1521620820
1 f none /usr/share/opensvc/lib/resDiskHpVm.py 0664 root root 4411 41130 1521620820
1 f none /usr/share/opensvc/lib/resDiskLdom.py 0664 root root 3439 29084 1521620820
1 f none /usr/share/opensvc/lib/resDiskLoop.py 0664 root root 568 43300 1521620820
1 f none /usr/share/opensvc/lib/resDiskLoopDarwin.py 0664 root root 1535 50915 1521620820
1 f none /usr/share/opensvc/lib/resDiskLoopFreeBSD.py 0664 root root 2162 27811 1521620820
1 f none /usr/share/opensvc/lib/resDiskLoopLinux.py 0664 root root 2336 47616 1521620820
1 f none /usr/share/opensvc/lib/resDiskLoopSunOS.py 0664 root root 552 41762 1521620820
1 f none /usr/share/opensvc/lib/resDiskLvLinux.py 0664 root root 4041 29295 1521620820
1 f none /usr/share/opensvc/lib/resDiskMdLinux.py 0664 root root 11846 445 1521620820
1 f none /usr/share/opensvc/lib/resDiskRadosLinux.py 0664 root root 8198 53444 1521620820
1 f none /usr/share/opensvc/lib/resDiskRaw.py 0664 root root 12698 56686 1521620820
1 f none /usr/share/opensvc/lib/resDiskRawHP-UX.py 0664 root root 856 54330 1521620820
1 f none /usr/share/opensvc/lib/resDiskRawLinux.py 0664 root root 9013 45745 1521620820
1 f none /usr/share/opensvc/lib/resDiskRawSunOS.py 0664 root root 939 64865 1521620820
1 f none /usr/share/opensvc/lib/resDiskVdisk.py 0664 root root 1835 842 1521620820
1 f none /usr/share/opensvc/lib/resDiskVgAIX.py 0664 root root 8377 58508 1521620820
1 f none /usr/share/opensvc/lib/resDiskVgAdvfs.py 0664 root root 2900 14664 1521620820
1 f none /usr/share/opensvc/lib/resDiskVgHP-UX.py 0664 root root 11604 33204 1521620820
1 f none /usr/share/opensvc/lib/resDiskVgLinux.py 0664 root root 8597 25624 1521620820
1 f none /usr/share/opensvc/lib/resDiskVgVeritas.py 0664 root root 3166 16531 1521620820
1 f none /usr/share/opensvc/lib/resDiskZfs.py 0664 root root 7181 31026 1521620820
1 f none /usr/share/opensvc/lib/resFs.py 0664 root root 7497 23131 1521620820
1 f none /usr/share/opensvc/lib/resFsAIX.py 0664 root root 4543 60455 1521620820
1 f none /usr/share/opensvc/lib/resFsDarwin.py 0664 root root 6914 29838 1521620820
1 f none /usr/share/opensvc/lib/resFsDir.py 0664 root root 3798 13359 1521620820
1 f none /usr/share/opensvc/lib/resFsDocker.py 0664 root root 1831 674 1521620820
1 f none /usr/share/opensvc/lib/resFsFreeBSD.py 0664 root root 4848 18489 1521620820
1 f none /usr/share/opensvc/lib/resFsHP-UX.py 0664 root root 3139 29012 1521620820
1 f none /usr/share/opensvc/lib/resFsLinux.py 0664 root root 20092 8427 1521620820
1 f none /usr/share/opensvc/lib/resFsOSF1.py 0664 root root 3954 25417 1521620820
1 f none /usr/share/opensvc/lib/resFsSunOS.py 0664 root root 6151 37274 1521620820
1 f none /usr/share/opensvc/lib/resFsWindows.py 0664 root root 4573 37493 1521620820
1 f none /usr/share/opensvc/lib/resIp.py 0664 root root 20551 41057 1521620820
1 f none /usr/share/opensvc/lib/resIpAIX.py 0664 root root 904 5802 1521620820
1 f none /usr/share/opensvc/lib/resIpAmazon.py 0664 root root 4275 53335 1521620820
1 f none /usr/share/opensvc/lib/resIpCni.py 0664 root root 12929 32369 1521620820
1 f none /usr/share/opensvc/lib/resIpCrossbow.py 0664 root root 5324 51473 1521620820
1 f none /usr/share/opensvc/lib/resIpDarwin.py 0664 root root 901 4974 1521620820
1 f none /usr/share/opensvc/lib/resIpDockerLinux.py 0664 root root 13744 38754 1521620820
1 f none /usr/share/opensvc/lib/resIpFreeBSD.py 0664 root root 920 6901 1521620820
1 f none /usr/share/opensvc/lib/resIpGce.py 0664 root root 6241 5502 1521620820
1 f none /usr/share/opensvc/lib/resIpHP-UX.py 0664 root root 1112 18823 1521620820
1 f none /usr/share/opensvc/lib/resIpLinux.py 0664 root root 1732 1951 1521620820
1 f none /usr/share/opensvc/lib/resIpOSF1.py 0664 root root 919 6744 1521620820
1 f none /usr/share/opensvc/lib/resIpSunOS.py 0664 root root 784 61725 1521620820
1 f none /usr/share/opensvc/lib/resIpWindows.py 0664 root root 2573 8432 1521620820
1 f none /usr/share/opensvc/lib/resIpZone.py 0664 root root 2110 17157 1521620820
1 f none /usr/share/opensvc/lib/resScsiReserv.py 0664 root root 8833 47940 1521620820
1 f none /usr/share/opensvc/lib/resScsiReservFreeBSD.py 0664 root root 257 21347 1521620820
1 f none /usr/share/opensvc/lib/resScsiReservHP-UX.py 0664 root root 5032 39678 1521620820
1 f none /usr/share/opensvc/lib/resScsiReservLinux.py 0664 root root 257 21347 1521620820
1 f none /usr/share/opensvc/lib/resScsiReservOSF1.py 0664 root root 6467 91 1521620820
1 f none /usr/share/opensvc/lib/resScsiReservSg.py 0664 root root 6006 57709 1521620820
1 f none /usr/share/opensvc/lib/resScsiReservSunOS.py 0664 root root 79 7221 1521620820
1 f none /usr/share/opensvc/lib/resShareNfsHP-UX.py 0664 root root 3662 64679 1521620820
1 f none /usr/share/opensvc/lib/resShareNfsLinux.py 0664 root root 4404 58192 1521620820
1 f none /usr/share/opensvc/lib/resShareNfsSunOS.py 0664 root root 3442 49166 1521620820
1 f none /usr/share/opensvc/lib/resSync.py 0664 root root 7355 24451 1521620820
1 f none /usr/share/opensvc/lib/resSyncBtrfs.py 0664 root root 15813 3990 1521620820
1 f none /usr/share/opensvc/lib/resSyncBtrfsSnap.py 0664 root root 6730 25862 1521620820
1 f none /usr/share/opensvc/lib/resSyncDcs.py 0664 root root 4230 57331 1521620820
1 f none /usr/share/opensvc/lib/resSyncDcsCkpt.py 0664 root root 6743 37423 1521620820
1 f none /usr/share/opensvc/lib/resSyncDcsSnap.py 0664 root root 6232 36256 1521620820
1 f none /usr/share/opensvc/lib/resSyncDds.py 0664 root root 13825 24061 1521620820
1 f none /usr/share/opensvc/lib/resSyncDocker.py 0664 root root 5518 9868 1521620820
1 f none /usr/share/opensvc/lib/resSyncEvasnap.py 0664 root root 10712 63857 1521620820
1 f none /usr/share/opensvc/lib/resSyncHp3par.py 0664 root root 10026 19464 1521620820
1 f none /usr/share/opensvc/lib/resSyncHp3parSnap.py 0664 root root 3373 54925 1521620820
1 f none /usr/share/opensvc/lib/resSyncIbmdsSnap.py 0664 root root 8639 25195 1521620820
1 f none /usr/share/opensvc/lib/resSyncNecIsmSnap.py 0664 root root 5722 16049 1521620820
1 f none /usr/share/opensvc/lib/resSyncNetapp.py 0664 root root 10144 16124 1521620820
1 f none /usr/share/opensvc/lib/resSyncNexenta.py 0664 root root 8292 13328 1521620820
1 f none /usr/share/opensvc/lib/resSyncRados.py 0664 root root 10473 12883 1521620820
1 f none /usr/share/opensvc/lib/resSyncRsync.py 0664 root root 16522 34320 1521620820
1 f none /usr/share/opensvc/lib/resSyncS3.py 0664 root root 9315 8609 1521620820
1 f none /usr/share/opensvc/lib/resSyncSymSrdfS.py 0664 root root 21519 42070 1521620820
1 f none /usr/share/opensvc/lib/resSyncSymclone.py 0664 root root 10364 45748 1521620820
1 f none /usr/share/opensvc/lib/resSyncSymcloneLinux.py 0664 root root 2473 44536 1521620820
1 f none /usr/share/opensvc/lib/resSyncZfs.py 0664 root root 15088 16962 1521620820
1 f none /usr/share/opensvc/lib/resSyncZfsSnap.py 0664 root root 5664 12945 1521620820
1 f none /usr/share/opensvc/lib/resTask.py 0664 root root 3692 18467 1521620820
1 f none /usr/share/opensvc/lib/resources.py 0664 root root 35737 2654 1521620820
1 f none /usr/share/opensvc/lib/resourceset.py 0664 root root 13352 54205 1521620820
1 f none /usr/share/opensvc/lib/snap.py 0664 root root 4322 55486 1521620820
1 f none /usr/share/opensvc/lib/snapAdvfsOSF1.py 0664 root root 3209 48800 1521620820
1 f none /usr/share/opensvc/lib/snapJfs2AIX.py 0664 root root 4107 32926 1521620820
1 f none /usr/share/opensvc/lib/snapLvmLinux.py 0664 root root 4504 64968 1521620820
1 f none /usr/share/opensvc/lib/snapVxfsHP-UX.py 0664 root root 2749 6374 1521620820
1 f none /usr/share/opensvc/lib/snapZfsSunOS.py 0664 root root 1445 47441 1521620820
1 f none /usr/share/opensvc/lib/svc.py 0664 root root 173742 25578 1521620820
1 f none /usr/share/opensvc/lib/svcBuilder.py 0664 root root 66934 49971 1521620820
1 f none /usr/share/opensvc/lib/svcdict.py 0664 root root 184494 36255 1521620820
1 f none /usr/share/opensvc/lib/svcmgr.py 0664 root root 9565 24279 1521620820
1 f none /usr/share/opensvc/lib/svcmgr_parser.py 0664 root root 46264 12361 1521620820
1 f none /usr/share/opensvc/lib/svcmon.py 0664 root root 3860 26658 1521620820
1 f none /usr/share/opensvc/lib/tabulate.py 0664 root root 26534 22047 1521620820
1 f none /usr/share/opensvc/lib/version.py 0644 root root 21 1388 1521621049
1 f none /usr/share/opensvc/lib/wmi.py 0664 root root 46934 3064 1521620820
1 f none /usr/share/opensvc/lib/xmlrpcClient.py 0664 root root 46786 59025 1521620820
1 d none /var/lib/opensvc 0755 root root
1 d none /var/lib/opensvc/cache 0755 root root
1 d none /var/lib/opensvc/compliance 0755 root root
1 d none /var/lib/opensvc/compliance/com.opensvc 0755 root root
1 f none /var/lib/opensvc/compliance/com.opensvc/ansible_playbook.py 0775 root root 5285 5233 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/authkey.py 0775 root root 17241 7192 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/bios.py 0775 root root 2165 27446 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/chkconfig.py 0775 root root 3667 1530 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/comp.py 0664 root root 15844 35528 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/cron.py 0775 root root 6862 10618 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/etcsystem.py 0775 root root 6722 9689 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/file.py 0775 root root 12408 50056 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/fileinc.py 0775 root root 8911 17597 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/fileprop.py 0775 root root 9631 27377 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/firmware.py 0775 root root 6707 19567 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/fs.py 0775 root root 22529 37954 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/group.py 0775 root root 7738 35340 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/group_membership.py 0775 root root 7892 62851 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/keyval.py 0775 root root 11686 35293 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/keyval_parser.py 0775 root root 6162 41502 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/linux.mpath.py 0775 root root 15677 40723 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/nodeconf.py 0775 root root 5929 23642 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/package.py 0775 root root 26262 61773 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/process.py 0775 root root 13316 54272 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/rc.py 0775 root root 4513 59049 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/remove_files.py 0775 root root 2367 47306 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/self.signed.cert.py 0775 root root 5369 59062 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/smfcfgs.py 0775 root root 14757 64492 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/sudoers.py 0775 root root 2376 45343 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/svcconf.py 0775 root root 9997 50975 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/symlink.py 0775 root root 3817 27588 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/sysctl.py 0775 root root 8354 6449 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/sysvinit.py 0775 root root 7817 31424 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/timedatectl.py 0775 root root 5731 16072 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/user.py 0775 root root 16544 63021 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/utilities.py 0664 root root 752 62301 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/volume_file.py 0775 root root 2988 45078 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/vuln.py 0775 root root 23120 7213 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/xinetd.py 0775 root root 5514 57669 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/yes 0775 root root 121 9914 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/zfs.py 0775 root root 8814 16796 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/zpool.py 0775 root root 5546 35703 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/zprop.py 0775 root root 4413 47974 1521620820
1 d none /var/log 0755 root sys
1 d none /var/log/opensvc 0755 root root
1 i pkginfo 211 17697 1521621050
1 i postinstall 671 61368 1521621049
1 i preinstall 1211 34464 1521621049
1 i preremove 403 31121 1521621049
  07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000b00000000TRAILER!!!                                                                                                                                                                                                                                                                                                                            0707010005c703000081a40000000000000000000000015ab2183a000000d3000000660000000000000000000000000000000800000000pkginfo   CLASSES=none
PSTAMP=OPENSVC-1.9,REV=1489
EMAIL=support@opensvc.com
VENDOR=http://www.opensvc.com
CATEGORY=application
VERSION=1.9,REV=1489
ARCH=all
NAME=Automation and configuration management agent
PKG=opensvc
 0707010005c702000081a40000000000000000000000015ab2183a0000e276000000660000000000000000000000000000000700000000pkgmap    : 1 14384
1 d none /etc/bash_completion.d 0755 root root
1 f none /etc/bash_completion.d/opensvc 0664 root root 30270 11459 1521620820
1 d none /etc/opensvc 0755 root root
1 s none /usr/bin/nodemgr=../share/opensvc/bin/opensvc
1 s none /usr/bin/svcmgr=../share/opensvc/bin/opensvc
1 s none /usr/bin/svcmon=../share/opensvc/bin/opensvc
1 d none /usr/share 0755 root sys
1 d none /usr/share/doc 0755 root other
1 d none /usr/share/doc/opensvc 0755 root root
1 f none /usr/share/doc/opensvc/AUTHORS 0664 root root 506 47637 1521620820
1 f none /usr/share/doc/opensvc/auth.conf 0664 root root 7306 56277 1521620820
1 f none /usr/share/doc/opensvc/copyright 0664 root root 2320 63869 1521620820
1 d none /usr/share/doc/opensvc/provisioning 0755 root root
1 f none /usr/share/doc/opensvc/provisioning/provisioning.agent.debian 0775 root root 2602 50922 1521620820
1 f none /usr/share/doc/opensvc/provisioning/provisioning.example 0664 root root 362 30449 1521620820
1 f none /usr/share/doc/opensvc/schedule 0664 root root 2290 30408 1521620820
1 f none /usr/share/doc/opensvc/template.comp_module.py 0775 root root 1232 29792 1521620820
1 f none /usr/share/doc/opensvc/template.comp_module.sh 0775 root root 419 29995 1521620820
1 f none /usr/share/doc/opensvc/template.node.arbitrator.conf.gz 0664 root root 699 21495 1521620820
1 f none /usr/share/doc/opensvc/template.node.asset.conf.gz 0664 root root 226 26587 1521620820
1 f none /usr/share/doc/opensvc/template.node.brocade.conf.gz 0664 root root 221 27994 1521620820
1 f none /usr/share/doc/opensvc/template.node.centera.conf.gz 0664 root root 219 26771 1521620820
1 f none /usr/share/doc/opensvc/template.node.checks.conf.gz 0664 root root 227 28795 1521620820
1 f none /usr/share/doc/opensvc/template.node.cluster.conf.gz 0664 root root 669 17026 1521620820
1 f none /usr/share/doc/opensvc/template.node.cni.conf.gz 0664 root root 232 27157 1521620820
1 f none /usr/share/doc/opensvc/template.node.compliance.conf.gz 0664 root root 471 55838 1521620820
1 f none /usr/share/doc/opensvc/template.node.dcs.conf.gz 0664 root root 217 25434 1521620820
1 f none /usr/share/doc/opensvc/template.node.dequeue_actions.conf.gz 0664 root root 227 27275 1521620820
1 f none /usr/share/doc/opensvc/template.node.disks.conf.gz 0664 root root 225 27300 1521620820
1 f none /usr/share/doc/opensvc/template.node.eva.conf.gz 0664 root root 217 24958 1521620820
1 f none /usr/share/doc/opensvc/template.node.freenas.conf.gz 0664 root root 222 28178 1521620820
1 f none /usr/share/doc/opensvc/template.node.gcedisks.conf.gz 0664 root root 223 27488 1521620820
1 f none /usr/share/doc/opensvc/template.node.hb.disk.conf.gz 0664 root root 349 43314 1521620820
1 f none /usr/share/doc/opensvc/template.node.hb.multicast.conf.gz 0664 root root 368 45690 1521620820
1 f none /usr/share/doc/opensvc/template.node.hb.relay.conf.gz 0664 root root 357 44347 1521620820
1 f none /usr/share/doc/opensvc/template.node.hb.unicast.conf.gz 0664 root root 383 48584 1521620820
1 f none /usr/share/doc/opensvc/template.node.hds.conf.gz 0664 root root 217 25873 1521620820
1 f none /usr/share/doc/opensvc/template.node.hp3par.conf.gz 0664 root root 223 27089 1521620820
1 f none /usr/share/doc/opensvc/template.node.ibmds.conf.gz 0664 root root 221 26204 1521620820
1 f none /usr/share/doc/opensvc/template.node.ibmsvc.conf.gz 0664 root root 220 26569 1521620820
1 f none /usr/share/doc/opensvc/template.node.listener.conf.gz 0664 root root 358 44106 1521620820
1 f none /usr/share/doc/opensvc/template.node.necism.conf.gz 0664 root root 220 25353 1521620820
1 f none /usr/share/doc/opensvc/template.node.node.conf.gz 0664 root root 2205 18609 1521620820
1 f none /usr/share/doc/opensvc/template.node.nsr.conf.gz 0664 root root 216 26033 1521620820
1 f none /usr/share/doc/opensvc/template.node.packages.conf.gz 0664 root root 229 25946 1521620820
1 f none /usr/share/doc/opensvc/template.node.patches.conf.gz 0664 root root 228 26607 1521620820
1 f none /usr/share/doc/opensvc/template.node.reboot.conf.gz 0664 root root 540 2880 1521620820
1 f none /usr/share/doc/opensvc/template.node.rotate_root_pw.conf.gz 0664 root root 227 28776 1521620820
1 f none /usr/share/doc/opensvc/template.node.stats.conf.gz 0664 root root 327 40293 1521620820
1 f none /usr/share/doc/opensvc/template.node.stats_collection.conf.gz 0664 root root 227 27131 1521620820
1 f none /usr/share/doc/opensvc/template.node.stonith.conf.gz 0664 root root 238 30771 1521620820
1 f none /usr/share/doc/opensvc/template.node.sym.conf.gz 0664 root root 217 25880 1521620820
1 f none /usr/share/doc/opensvc/template.node.syslog.conf.gz 0664 root root 418 52917 1521620820
1 f none /usr/share/doc/opensvc/template.node.sysreport.conf.gz 0664 root root 266 31421 1521620820
1 f none /usr/share/doc/opensvc/template.node.vioserver.conf.gz 0664 root root 221 27679 1521620820
1 f none /usr/share/doc/opensvc/template.node.vnx.conf.gz 0664 root root 220 27022 1521620820
1 f none /usr/share/doc/opensvc/template.node.xtremio.conf.gz 0664 root root 222 27207 1521620820
1 f none /usr/share/doc/opensvc/template.service.DEFAULT.conf.gz 0664 root root 8312 3859 1521620820
1 f none /usr/share/doc/opensvc/template.service.app.conf.gz 0664 root root 3554 57808 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.amazon.conf.gz 0664 root root 3641 7077 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.docker.conf.gz 0664 root root 3761 12308 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.esx.conf.gz 0664 root root 3559 59222 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.hpvm.conf.gz 0664 root root 3462 44776 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.jail.conf.gz 0664 root root 3562 53888 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.kvm.conf.gz 0664 root root 3598 55905 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.ldom.conf.gz 0664 root root 3462 44371 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.lxc.conf.gz 0664 root root 3679 12270 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.lxd.conf.gz 0664 root root 3577 59798 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.openstack.conf.gz 0664 root root 3642 64206 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.ovm.conf.gz 0664 root root 3639 64678 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.srp.conf.gz 0664 root root 3574 53140 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.vbox.conf.gz 0664 root root 3462 40879 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.vcloud.conf.gz 0664 root root 3681 7686 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.vz.conf.gz 0664 root root 3549 53379 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.xen.conf.gz 0664 root root 3598 58732 1521620820
1 f none /usr/share/doc/opensvc/template.service.container.zone.conf.gz 0664 root root 3827 19003 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.amazon.conf.gz 0664 root root 3531 53197 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.disk.conf.gz 0664 root root 3522 59416 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.drbd.conf.gz 0664 root root 3424 44120 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.gce.conf.gz 0664 root root 3854 24945 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.loop.conf.gz 0664 root root 3375 30561 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.lv.conf.gz 0664 root root 3498 55617 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.lvm.conf.gz 0664 root root 3515 50898 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.md.conf.gz 0664 root root 3507 55481 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.rados.conf.gz 0664 root root 3591 56411 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.raw.conf.gz 0664 root root 3719 7315 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.vdisk.conf.gz 0664 root root 3375 43097 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.veritas.conf.gz 0664 root root 3329 34139 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.vg.conf.gz 0664 root root 3515 59359 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.vmdg.conf.gz 0664 root root 3354 45007 1521620820
1 f none /usr/share/doc/opensvc/template.service.disk.zpool.conf.gz 0664 root root 3433 42781 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.bfs.conf.gz 0664 root root 3757 34062 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.btrfs.conf.gz 0664 root root 3759 23482 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.conf.gz 0664 root root 3858 26606 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.directory.conf.gz 0664 root root 3731 14402 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.ext.conf.gz 0664 root root 3758 34128 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.ext2.conf.gz 0664 root root 3758 34127 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.ext3.conf.gz 0664 root root 3758 33261 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.ext4.conf.gz 0664 root root 3759 28974 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.f2fs.conf.gz 0664 root root 3758 29571 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.gfs.conf.gz 0664 root root 3757 33996 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.gfs2.conf.gz 0664 root root 3758 33767 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.gpfs.conf.gz 0664 root root 3758 32500 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.hfs.conf.gz 0664 root root 3757 33916 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.hfsplus.conf.gz 0664 root root 3760 25316 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.hpfs.conf.gz 0664 root root 3758 32277 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.jffs.conf.gz 0664 root root 3758 31335 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.jffs2.conf.gz 0664 root root 3760 31955 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.jfs.conf.gz 0664 root root 3758 25917 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.jfs2.conf.gz 0664 root root 3759 25884 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.logfs.conf.gz 0664 root root 3757 25393 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.minix.conf.gz 0664 root root 3759 32599 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.msdos.conf.gz 0664 root root 3759 29043 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.nilfs.conf.gz 0664 root root 3759 28282 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.ntfs.conf.gz 0664 root root 3758 30492 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.ocfs.conf.gz 0664 root root 3758 23419 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.ocfs2.conf.gz 0664 root root 3759 32827 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.qnx4.conf.gz 0664 root root 3759 31488 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.reiserfs.conf.gz 0664 root root 3761 28576 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.reiserfs4.conf.gz 0664 root root 3762 28956 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.tux3.conf.gz 0664 root root 3759 28325 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.ufs.conf.gz 0664 root root 3757 30532 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.ufs2.conf.gz 0664 root root 3758 31109 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.umsdos.conf.gz 0664 root root 3759 30298 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.vfat.conf.gz 0664 root root 3758 32094 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.vxfs.conf.gz 0664 root root 3758 34295 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.xfs.conf.gz 0664 root root 3758 29104 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.xia.conf.gz 0664 root root 3757 34141 1521620820
1 f none /usr/share/doc/opensvc/template.service.fs.zfs.conf.gz 0664 root root 3614 2021 1521620820
1 f none /usr/share/doc/opensvc/template.service.ip.amazon.conf.gz 0664 root root 3878 28934 1521620820
1 f none /usr/share/doc/opensvc/template.service.ip.cni.conf.gz 0664 root root 3737 5003 1521620820
1 f none /usr/share/doc/opensvc/template.service.ip.conf.gz 0664 root root 3833 14243 1521620820
1 f none /usr/share/doc/opensvc/template.service.ip.crossbow.conf.gz 0664 root root 3708 13070 1521620820
1 f none /usr/share/doc/opensvc/template.service.ip.docker.conf.gz 0664 root root 3789 21073 1521620820
1 f none /usr/share/doc/opensvc/template.service.ip.gce.conf.gz 0664 root root 3728 5995 1521620820
1 f none /usr/share/doc/opensvc/template.service.share.nfs.conf.gz 0664 root root 2909 40639 1521620820
1 f none /usr/share/doc/opensvc/template.service.subset.conf.gz 0664 root root 1175 20942 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.btrfs.conf.gz 0664 root root 3644 63704 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.btrfssnap.conf.gz 0664 root root 3739 10327 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.dcsckpt.conf.gz 0664 root root 3698 6270 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.dcssnap.conf.gz 0664 root root 3645 62567 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.dds.conf.gz 0664 root root 4046 52027 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.docker.conf.gz 0664 root root 3559 47971 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.evasnap.conf.gz 0664 root root 3710 10857 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.hp3par.conf.gz 0664 root root 3718 6604 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.hp3parsnap.conf.gz 0664 root root 3596 58342 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.ibmdssnap.conf.gz 0664 root root 3725 4251 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.necismsnap.conf.gz 0664 root root 3505 45767 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.netapp.conf.gz 0664 root root 3707 7167 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.rados.conf.gz 0664 root root 3502 44271 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.rsync.conf.gz 0664 root root 4243 8374 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.s3.conf.gz 0664 root root 3934 41594 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.symclone.conf.gz 0664 root root 3796 17981 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.symsnap.conf.gz 0664 root root 3772 7182 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.symsrdfs.conf.gz 0664 root root 3669 65428 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.zfs.conf.gz 0664 root root 3866 26308 1521620820
1 f none /usr/share/doc/opensvc/template.service.sync.zfssnap.conf.gz 0664 root root 3782 12288 1521620820
1 f none /usr/share/doc/opensvc/template.service.task.conf.gz 0664 root root 3204 9964 1521620820
1 f none /usr/share/doc/opensvc/template.stonith.callout.conf.gz 0664 root root 1666 10752 1521620820
1 f none /usr/share/doc/opensvc/template.stonith.ilo.conf.gz 0664 root root 1699 21374 1521620820
1 f none /usr/share/doc/opensvc/template.svc.prov.zfs.conf.gz 0664 root root 249 30291 1521620820
1 f none /usr/share/doc/opensvc/template.svc.prov.zpool.conf.gz 0664 root root 172 20316 1521620820
1 f none /usr/share/doc/opensvc/template.sysreport.conf.gz 0664 root root 414 50775 1521620820
1 d none /usr/share/man 0755 root bin
1 d none /usr/share/man/man1 0755 root bin
1 f none /usr/share/man/man1/nodemgr.1.gz 0664 root root 6840 21952 1521620820
1 f none /usr/share/man/man1/svcmgr.1.gz 0664 root root 10034 21933 1521620820
1 f none /usr/share/man/man1/svcmon.1.gz 0664 root root 959 55183 1521620820
1 d none /usr/share/opensvc 0755 root root
1 d none /usr/share/opensvc/bin 0755 root root
1 d none /usr/share/opensvc/bin/init 0755 root root
1 f none /usr/share/opensvc/bin/init/darwin.com.opensvc.svcmgr.plist 0664 root root 597 51671 1521620820
1 f none /usr/share/opensvc/bin/init/opensvc.agent.xml 0664 root root 2097 49739 1521620820
1 f none /usr/share/opensvc/bin/init/opensvc.init.AIX 0775 root root 459 36992 1521620820
1 f none /usr/share/opensvc/bin/init/opensvc.init.Darwin 0775 root root 830 1316 1521620820
1 f none /usr/share/opensvc/bin/init/opensvc.init.FreeBSD 0775 root root 572 44763 1521620820
1 f none /usr/share/opensvc/bin/init/opensvc.init.OSF1 0775 root root 484 38126 1521620820
1 f none /usr/share/opensvc/bin/init/opensvc.init.SunOS 0775 root root 665 53113 1521620820
1 f none /usr/share/opensvc/bin/init/opensvc.init.debian 0775 root root 723 54824 1521620820
1 f none /usr/share/opensvc/bin/init/opensvc.init.hpux 0775 root root 962 9971 1521620820
1 f none /usr/share/opensvc/bin/init/opensvc.init.redhat 0775 root root 841 2538 1521620820
1 f none /usr/share/opensvc/bin/init/opensvc.init.suse 0775 root root 727 55057 1521620820
1 f none /usr/share/opensvc/bin/init/systemd.opensvc-agent.service 0664 root root 513 48250 1521620820
1 f none /usr/share/opensvc/bin/init/systemd.opensvc-services.service 0664 root root 354 33229 1521620820
1 s none /usr/share/opensvc/bin/nodemgr=opensvc
1 f none /usr/share/opensvc/bin/opensvc 0775 root root 1126 22334 1521620820
1 f none /usr/share/opensvc/bin/postinstall 0775 root root 41509 25465 1521620820
1 f none /usr/share/opensvc/bin/preinstall 0775 root root 1211 34464 1521620820
1 f none /usr/share/opensvc/bin/preuninstall 0775 root root 403 31121 1521620820
1 s none /usr/share/opensvc/bin/svcmgr=opensvc
1 s none /usr/share/opensvc/bin/svcmon=opensvc
1 d none /usr/share/opensvc/lib 0755 root root
1 f none /usr/share/opensvc/lib/checkAdvfsUsageOSF1.py 0664 root root 887 61564 1521620820
1 f none /usr/share/opensvc/lib/checkBtrfsDevStatsLinux.py 0664 root root 2505 45928 1521620820
1 f none /usr/share/opensvc/lib/checkEthHP-UX.py 0664 root root 2795 43220 1521620820
1 f none /usr/share/opensvc/lib/checkEthLinux.py 0664 root root 2848 53741 1521620820
1 f none /usr/share/opensvc/lib/checkEthSunOS.py 0664 root root 10182 13886 1521620820
1 f none /usr/share/opensvc/lib/checkFmFmadm.py 0664 root root 1049 11080 1521620820
1 s none /usr/share/opensvc/lib/checkFmFmadmSunOS.py=checkFmFmadm.py
1 f none /usr/share/opensvc/lib/checkFmOpenManage.py 0664 root root 1640 50564 1521620820
1 s none /usr/share/opensvc/lib/checkFmOpenManageLinux.py=checkFmOpenManage.py
1 f none /usr/share/opensvc/lib/checkFsInodeAIX.py 0664 root root 995 65387 1521620820
1 f none /usr/share/opensvc/lib/checkFsInodeDarwin.py 0664 root root 959 64537 1521620820
1 f none /usr/share/opensvc/lib/checkFsInodeHP-UX.py 0664 root root 1370 21841 1521620820
1 f none /usr/share/opensvc/lib/checkFsInodeLinux.py 0664 root root 1780 53406 1521620820
1 f none /usr/share/opensvc/lib/checkFsInodeSunOS.py 0664 root root 1091 6327 1521620820
1 f none /usr/share/opensvc/lib/checkFsUsageAIX.py 0664 root root 995 65370 1521620820
1 f none /usr/share/opensvc/lib/checkFsUsageDarwin.py 0664 root root 1468 34105 1521620820
1 f none /usr/share/opensvc/lib/checkFsUsageFreeBSD.py 0664 root root 1468 34105 1521620820
1 f none /usr/share/opensvc/lib/checkFsUsageHP-UX.py 0664 root root 1468 34105 1521620820
1 f none /usr/share/opensvc/lib/checkFsUsageLinux.py 0664 root root 2062 3189 1521620820
1 f none /usr/share/opensvc/lib/checkFsUsageSunOS.py 0664 root root 1488 28806 1521620820
1 f none /usr/share/opensvc/lib/checkFsUsageWindows.py 0664 root root 944 377 1521620820
1 f none /usr/share/opensvc/lib/checkLagHP-UX.py 0664 root root 1895 60326 1521620820
1 f none /usr/share/opensvc/lib/checkLagLinux.py 0664 root root 4063 20259 1521620820
1 f none /usr/share/opensvc/lib/checkLagSunOS.py 0664 root root 6841 11782 1521620820
1 f none /usr/share/opensvc/lib/checkLagWindows.py 0664 root root 763 53693 1521620820
1 f none /usr/share/opensvc/lib/checkMceLinux.py 0664 root root 1753 54530 1521620820
1 f none /usr/share/opensvc/lib/checkMpathAIX.py 0664 root root 2272 17452 1521620820
1 f none /usr/share/opensvc/lib/checkMpathHP-UX.py 0664 root root 2335 23985 1521620820
1 f none /usr/share/opensvc/lib/checkMpathLinux.py 0664 root root 4337 15881 1521620820
1 f none /usr/share/opensvc/lib/checkMpathOSF1.py 0664 root root 867 60569 1521620820
1 f none /usr/share/opensvc/lib/checkMpathPowerpath.py 0664 root root 2828 45262 1521620820
1 s none /usr/share/opensvc/lib/checkMpathPowerpathAIX.py=checkMpathPowerpath.py
1 s none /usr/share/opensvc/lib/checkMpathPowerpathHP-UX.py=checkMpathPowerpath.py
1 s none /usr/share/opensvc/lib/checkMpathPowerpathLinux.py=checkMpathPowerpath.py
1 s none /usr/share/opensvc/lib/checkMpathPowerpathSunOS.py=checkMpathPowerpath.py
1 f none /usr/share/opensvc/lib/checkMpathSunOS.py 0664 root root 2608 34126 1521620820
1 f none /usr/share/opensvc/lib/checkMpathWindows.py 0664 root root 1440 33261 1521620820
1 f none /usr/share/opensvc/lib/checkNumaLinux.py 0664 root root 1419 27818 1521620820
1 f none /usr/share/opensvc/lib/checkRaidMegaRaid.py 0664 root root 4594 39091 1521620820
1 s none /usr/share/opensvc/lib/checkRaidMegaRaidLinux.py=checkRaidMegaRaid.py
1 s none /usr/share/opensvc/lib/checkRaidMegaRaidSunOS.py=checkRaidMegaRaid.py
1 f none /usr/share/opensvc/lib/checkRaidSas2.py 0664 root root 3777 57148 1521620820
1 s none /usr/share/opensvc/lib/checkRaidSas2Linux.py=checkRaidSas2.py
1 s none /usr/share/opensvc/lib/checkRaidSas2SunOS.py=checkRaidSas2.py
1 f none /usr/share/opensvc/lib/checkRaidSmartArray.py 0664 root root 3095 18409 1521620820
1 s none /usr/share/opensvc/lib/checkRaidSmartArrayLinux.py=checkRaidSmartArray.py
1 s none /usr/share/opensvc/lib/checkRaidSmartArraySunOS.py=checkRaidSmartArray.py
1 f none /usr/share/opensvc/lib/checkRaidSmartArrayWindows.py 0664 root root 520 39436 1521620820
1 f none /usr/share/opensvc/lib/checkSync.py 0664 root root 1248 22180 1521620820
1 f none /usr/share/opensvc/lib/checkVgUsageAIX.py 0664 root root 1255 18069 1521620820
1 f none /usr/share/opensvc/lib/checkVgUsageHP-UX.py 0664 root root 1772 58287 1521620820
1 f none /usr/share/opensvc/lib/checkVgUsageLinux.py 0664 root root 1210 16640 1521620820
1 s none /usr/share/opensvc/lib/checkZfsUsageFreeBSD.py=checkZfsUsageSunOS.py
1 s none /usr/share/opensvc/lib/checkZfsUsageLinux.py=checkZfsUsageSunOS.py
1 f none /usr/share/opensvc/lib/checkZfsUsageSunOS.py 0664 root root 3085 9389 1521620820
1 s none /usr/share/opensvc/lib/checkZpoolFreeBSD.py=checkZpoolSunOS.py
1 s none /usr/share/opensvc/lib/checkZpoolLinux.py=checkZpoolSunOS.py
1 f none /usr/share/opensvc/lib/checkZpoolSunOS.py 0664 root root 2197 12700 1521620820
1 f none /usr/share/opensvc/lib/checks.py 0664 root root 5247 37281 1521620820
1 f none /usr/share/opensvc/lib/collector.py 0664 root root 11257 60807 1521620820
1 f none /usr/share/opensvc/lib/comm.py 0664 root root 20721 43263 1521620820
1 f none /usr/share/opensvc/lib/compliance.py 0664 root root 28760 39752 1521620820
1 f none /usr/share/opensvc/lib/converters.py 0664 root root 6730 22756 1521620820
1 f none /usr/share/opensvc/lib/extconfig.py 0664 root root 43302 35491 1521620820
1 f none /usr/share/opensvc/lib/forest.py 0664 root root 13364 9669 1521620820
1 f none /usr/share/opensvc/lib/freezer.py 0664 root root 2171 31605 1521620820
1 f none /usr/share/opensvc/lib/hb.py 0664 root root 11664 22564 1521620820
1 f none /usr/share/opensvc/lib/hb_disk.py 0664 root root 11638 48348 1521620820
1 f none /usr/share/opensvc/lib/hb_mcast.py 0664 root root 6839 39478 1521620820
1 f none /usr/share/opensvc/lib/hb_relay.py 0664 root root 7016 48013 1521620820
1 f none /usr/share/opensvc/lib/hb_ucast.py 0664 root root 7668 33064 1521620820
1 f none /usr/share/opensvc/lib/hostidAIX.py 0664 root root 285 21867 1521620820
1 f none /usr/share/opensvc/lib/hostidDarwin.py 0664 root root 66 5626 1521620820
1 s none /usr/share/opensvc/lib/hostidFreeBSD.py=hostidLinux.py
1 f none /usr/share/opensvc/lib/hostidHP-UX.py 0664 root root 381 29514 1521620820
1 f none /usr/share/opensvc/lib/hostidLinux.py 0664 root root 66 5626 1521620820
1 f none /usr/share/opensvc/lib/hostidOSF1.py 0664 root root 66 5626 1521620820
1 f none /usr/share/opensvc/lib/hostidSunOS.py 0664 root root 66 5626 1521620820
1 f none /usr/share/opensvc/lib/hostidWindows.py 0664 root root 66 5626 1521620820
1 f none /usr/share/opensvc/lib/ipaddress.py 0664 root root 79904 11072 1521620820
1 d none /usr/share/opensvc/lib/json_delta 0755 root root
1 f none /usr/share/opensvc/lib/json_delta/__init__.py 0664 root root 9208 47046 1521620820
1 f none /usr/share/opensvc/lib/json_delta/_diff.py 0664 root root 16578 38614 1521620820
1 f none /usr/share/opensvc/lib/json_delta/_patch.py 0664 root root 2442 60988 1521620820
1 f none /usr/share/opensvc/lib/json_delta/_udiff.py 0664 root root 18888 45438 1521620820
1 f none /usr/share/opensvc/lib/json_delta/_upatch.py 0664 root root 7922 20585 1521620820
1 f none /usr/share/opensvc/lib/json_delta/_util.py 0664 root root 21132 8738 1521620820
1 f none /usr/share/opensvc/lib/keywords.py 0664 root root 16215 8479 1521620820
1 f none /usr/share/opensvc/lib/lock.py 0664 root root 6493 26880 1521620820
1 f none /usr/share/opensvc/lib/node.py 0664 root root 150104 7563 1521620820
1 f none /usr/share/opensvc/lib/nodeDarwin.py 0664 root root 244 17954 1521620820
1 f none /usr/share/opensvc/lib/nodeHP-UX.py 0664 root root 252 17990 1521620820
1 f none /usr/share/opensvc/lib/nodeLinux.py 0664 root root 1121 15189 1521620820
1 f none /usr/share/opensvc/lib/nodeSunOS.py 0664 root root 1560 46090 1521620820
1 f none /usr/share/opensvc/lib/nodedict.py 0664 root root 44958 55241 1521620820
1 f none /usr/share/opensvc/lib/nodemgr.py 0664 root root 2565 6429 1521620820
1 f none /usr/share/opensvc/lib/nodemgr_parser.py 0664 root root 36829 19889 1521620820
1 f none /usr/share/opensvc/lib/osvcd.py 0664 root root 13216 53436 1521620820
1 f none /usr/share/opensvc/lib/osvcd_collector.py 0664 root root 6822 56747 1521620820
1 f none /usr/share/opensvc/lib/osvcd_dns.py 0664 root root 14782 34654 1521620820
1 f none /usr/share/opensvc/lib/osvcd_lsnr.py 0664 root root 26959 63995 1521620820
1 f none /usr/share/opensvc/lib/osvcd_mon.py 0664 root root 98380 15497 1521620820
1 f none /usr/share/opensvc/lib/osvcd_scheduler.py 0664 root root 6086 39507 1521620820
1 f none /usr/share/opensvc/lib/osvcd_shared.py 0664 root root 31767 59703 1521620820
1 f none /usr/share/opensvc/lib/osvcd_winservice.py 0664 root root 2221 46683 1521620820
1 f none /usr/share/opensvc/lib/provApp.py 0664 root root 234 17832 1521620820
1 f none /usr/share/opensvc/lib/provContainerAmazon.py 0664 root root 1314 39736 1521620820
1 f none /usr/share/opensvc/lib/provContainerDocker.py 0664 root root 267 21179 1521620820
1 f none /usr/share/opensvc/lib/provContainerLxd.py 0664 root root 1136 21856 1521620820
1 f none /usr/share/opensvc/lib/provContainerOpenstack.py 0664 root root 555 46693 1521620820
1 f none /usr/share/opensvc/lib/provContainerVcloud.py 0664 root root 488 40948 1521620820
1 f none /usr/share/opensvc/lib/provContainerZone.py 0664 root root 14571 28903 1521620820
1 f none /usr/share/opensvc/lib/provDiskAmazon.py 0664 root root 1768 3253 1521620820
1 f none /usr/share/opensvc/lib/provDiskDisk.py 0664 root root 2132 31823 1521620820
1 f none /usr/share/opensvc/lib/provDiskGce.py 0664 root root 2469 51225 1521620820
1 f none /usr/share/opensvc/lib/provDiskLoopDarwin.py 0664 root root 1445 42699 1521620820
1 f none /usr/share/opensvc/lib/provDiskLoopLinux.py 0664 root root 1445 42699 1521620820
1 f none /usr/share/opensvc/lib/provDiskLvHP-UX.py 0664 root root 1361 39551 1521620820
1 f none /usr/share/opensvc/lib/provDiskLvLinux.py 0664 root root 6372 64369 1521620820
1 f none /usr/share/opensvc/lib/provDiskMdLinux.py 0664 root root 3430 55846 1521620820
1 f none /usr/share/opensvc/lib/provDiskRadosLinux.py 0664 root root 1076 18009 1521620820
1 f none /usr/share/opensvc/lib/provDiskRaw.py 0664 root root 129 11392 1521620820
1 f none /usr/share/opensvc/lib/provDiskVgHP-UX.py 0664 root root 1774 61052 1521620820
1 f none /usr/share/opensvc/lib/provDiskVgLinux.py 0664 root root 3708 4132 1521620820
1 f none /usr/share/opensvc/lib/provDiskZpool.py 0664 root root 934 5046 1521620820
1 f none /usr/share/opensvc/lib/provFs.py 0664 root root 5446 5978 1521620820
1 f none /usr/share/opensvc/lib/provFsBtrfs.py 0664 root root 3811 19292 1521620820
1 f none /usr/share/opensvc/lib/provFsDir.py 0664 root root 263 20875 1521620820
1 f none /usr/share/opensvc/lib/provFsDocker.py 0664 root root 1186 26817 1521620820
1 f none /usr/share/opensvc/lib/provFsExt2.py 0664 root root 106 7982 1521620820
1 f none /usr/share/opensvc/lib/provFsExt3.py 0664 root root 106 7983 1521620820
1 f none /usr/share/opensvc/lib/provFsExt4.py 0664 root root 106 7984 1521620820
1 f none /usr/share/opensvc/lib/provFsHfs.py 0664 root root 2581 44215 1521620820
1 f none /usr/share/opensvc/lib/provFsVxfs.py 0664 root root 130 9689 1521620820
1 f none /usr/share/opensvc/lib/provFsXfs.py 0664 root root 108 8216 1521620820
1 f none /usr/share/opensvc/lib/provFsZfs.py 0664 root root 2066 30681 1521620820
1 f none /usr/share/opensvc/lib/provIp.py 0664 root root 1029 12654 1521620820
1 f none /usr/share/opensvc/lib/provIpAmazon.py 0664 root root 3290 54620 1521620820
1 f none /usr/share/opensvc/lib/provKvm.py 0664 root root 4517 64748 1521620820
1 f none /usr/share/opensvc/lib/provLxc.py 0664 root root 8603 10370 1521620820
1 f none /usr/share/opensvc/lib/provShare.py 0664 root root 189 15547 1521620820
1 f none /usr/share/opensvc/lib/provSrp.py 0664 root root 2940 9313 1521620820
1 f none /usr/share/opensvc/lib/provisioning.py 0664 root root 937 5426 1521620820
1 d none /usr/share/opensvc/lib/pyaes 0755 root root
1 f none /usr/share/opensvc/lib/pyaes/__init__.py 0664 root root 2087 40499 1521620820
1 f none /usr/share/opensvc/lib/pyaes/aes.py 0664 root root 60310 16766 1521620820
1 f none /usr/share/opensvc/lib/pyaes/blockfeeder.py 0664 root root 8119 11752 1521620820
1 f none /usr/share/opensvc/lib/pyaes/util.py 0664 root root 2050 33260 1521620820
1 f none /usr/share/opensvc/lib/rcAddService.py 0664 root root 95 7538 1521620820
1 f none /usr/share/opensvc/lib/rcAdvfs.py 0664 root root 6389 37737 1521620820
1 f none /usr/share/opensvc/lib/rcAmazon.py 0664 root root 1472 47966 1521620820
1 f none /usr/share/opensvc/lib/rcAsset.py 0664 root root 21179 71 1521620820
1 f none /usr/share/opensvc/lib/rcAssetAIX.py 0664 root root 3130 22136 1521620820
1 f none /usr/share/opensvc/lib/rcAssetDarwin.py 0664 root root 3178 32595 1521620820
1 f none /usr/share/opensvc/lib/rcAssetFreeBSD.py 0664 root root 2096 15927 1521620820
1 f none /usr/share/opensvc/lib/rcAssetHP-UX.py 0664 root root 8452 54560 1521620820
1 f none /usr/share/opensvc/lib/rcAssetLinux.py 0664 root root 21112 36970 1521620820
1 f none /usr/share/opensvc/lib/rcAssetOSF1.py 0664 root root 10914 31165 1521620820
1 f none /usr/share/opensvc/lib/rcAssetSunOS.py 0664 root root 8039 27545 1521620820
1 f none /usr/share/opensvc/lib/rcAssetWindows.py 0664 root root 8832 23619 1521620820
1 f none /usr/share/opensvc/lib/rcBrocade.py 0664 root root 4263 49336 1521620820
1 f none /usr/share/opensvc/lib/rcBtrfs.py 0664 root root 13541 53538 1521620820
1 f none /usr/share/opensvc/lib/rcCentera.py 0664 root root 3078 35176 1521620820
1 f none /usr/share/opensvc/lib/rcCloud.py 0664 root root 444 33680 1521620820
1 f none /usr/share/opensvc/lib/rcCloudAmazon.py 0664 root root 1283 40589 1521620820
1 f none /usr/share/opensvc/lib/rcCloudGandi.py 0664 root root 1352 38858 1521620820
1 f none /usr/share/opensvc/lib/rcCloudOpenstack.py 0664 root root 2152 37985 1521620820
1 f none /usr/share/opensvc/lib/rcCloudVcloud.py 0664 root root 1900 16684 1521620820
1 f none /usr/share/opensvc/lib/rcCollectorCli.py 0775 root root 97155 17295 1521620820
1 f none /usr/share/opensvc/lib/rcColor.py 0664 root root 9587 35561 1521620820
1 f none /usr/share/opensvc/lib/rcConfigParser.py 0664 root root 2075 19030 1521620820
1 f none /usr/share/opensvc/lib/rcDcs.py 0664 root root 9489 24840 1521620820
1 f none /usr/share/opensvc/lib/rcDevTree.py 0664 root root 13185 53937 1521620820
1 f none /usr/share/opensvc/lib/rcDevTreeAIX.py 0664 root root 2040 16817 1521620820
1 f none /usr/share/opensvc/lib/rcDevTreeHP-UX.py 0664 root root 6121 31912 1521620820
1 f none /usr/share/opensvc/lib/rcDevTreeLinux.py 0664 root root 20336 33888 1521620820
1 f none /usr/share/opensvc/lib/rcDevTreeOSF1.py 0664 root root 3072 11174 1521620820
1 f none /usr/share/opensvc/lib/rcDevTreeSunOS.py 0664 root root 10369 6898 1521620820
1 f none /usr/share/opensvc/lib/rcDevTreeVeritas.py 0664 root root 8181 26525 1521620820
1 f none /usr/share/opensvc/lib/rcDevTreeWindows.py 0664 root root 619 47948 1521620820
1 f none /usr/share/opensvc/lib/rcDiskInfo.py 0664 root root 669 48059 1521620820
1 f none /usr/share/opensvc/lib/rcDiskInfoAIX.py 0664 root root 2098 15715 1521620820
1 f none /usr/share/opensvc/lib/rcDiskInfoDarwin.py 0664 root root 65 5711 1521620820
1 f none /usr/share/opensvc/lib/rcDiskInfoFreeBSD.py 0664 root root 538 41094 1521620820
1 f none /usr/share/opensvc/lib/rcDiskInfoHP-UX.py 0664 root root 6697 7048 1521620820
1 f none /usr/share/opensvc/lib/rcDiskInfoLinux.py 0664 root root 12649 35348 1521620820
1 f none /usr/share/opensvc/lib/rcDiskInfoOSF1.py 0664 root root 2866 64303 1521620820
1 f none /usr/share/opensvc/lib/rcDiskInfoSunOS.py 0664 root root 5106 12482 1521620820
1 f none /usr/share/opensvc/lib/rcDiskInfoWindows.py 0664 root root 3598 39919 1521620820
1 f none /usr/share/opensvc/lib/rcDocker.py 0664 root root 33031 47112 1521620820
1 f none /usr/share/opensvc/lib/rcEmcVnx.py 0664 root root 4817 26908 1521620820
1 f none /usr/share/opensvc/lib/rcEthtool.py 0664 root root 1737 56779 1521620820
1 f none /usr/share/opensvc/lib/rcEva.py 0664 root root 4523 5619 1521620820
1 f none /usr/share/opensvc/lib/rcExceptions.py 0664 root root 3505 20406 1521620820
1 f none /usr/share/opensvc/lib/rcFreenas.py 0664 root root 32016 54639 1521620820
1 f none /usr/share/opensvc/lib/rcGce.py 0664 root root 777 58025 1521620820
1 f none /usr/share/opensvc/lib/rcGceDisks.py 0664 root root 1254 30197 1521620820
1 f none /usr/share/opensvc/lib/rcGlobalEnv.py 0664 root root 7471 59385 1521620820
1 f none /usr/share/opensvc/lib/rcHds.py 0664 root root 25605 54056 1521620820
1 f none /usr/share/opensvc/lib/rcHp3par.py 0664 root root 15029 32282 1521620820
1 f none /usr/share/opensvc/lib/rcIbmDs.py 0664 root root 3458 59022 1521620820
1 f none /usr/share/opensvc/lib/rcIbmSvc.py 0664 root root 3014 26759 1521620820
1 f none /usr/share/opensvc/lib/rcIfconfig.py 0664 root root 4636 4111 1521620820
1 f none /usr/share/opensvc/lib/rcIfconfigAIX.py 0664 root root 2769 51138 1521620820
1 f none /usr/share/opensvc/lib/rcIfconfigDarwin.py 0664 root root 2932 59705 1521620820
1 f none /usr/share/opensvc/lib/rcIfconfigFreeBSD.py 0664 root root 1953 59987 1521620820
1 f none /usr/share/opensvc/lib/rcIfconfigHP-UX.py 0664 root root 4987 55926 1521620820
1 f none /usr/share/opensvc/lib/rcIfconfigLinux.py 0664 root root 9353 59146 1521620820
1 f none /usr/share/opensvc/lib/rcIfconfigOSF1.py 0664 root root 3138 7251 1521620820
1 f none /usr/share/opensvc/lib/rcIfconfigSunOS.py 0664 root root 4834 54113 1521620820
1 f none /usr/share/opensvc/lib/rcIfconfigWindows.py 0664 root root 1334 30901 1521620820
1 f none /usr/share/opensvc/lib/rcLogger.py 0664 root root 7438 18009 1521620820
1 f none /usr/share/opensvc/lib/rcLoopDarwin.py 0664 root root 854 64731 1521620820
1 f none /usr/share/opensvc/lib/rcLoopLinux.py 0664 root root 1549 50509 1521620820
1 f none /usr/share/opensvc/lib/rcLvmAIX.py 0664 root root 7970 44000 1521620820
1 f none /usr/share/opensvc/lib/rcMd5.py 0664 root root 9367 14807 1521620820
1 f none /usr/share/opensvc/lib/rcMounts.py 0664 root root 2505 50756 1521620820
1 f none /usr/share/opensvc/lib/rcMountsAIX.py 0664 root root 3445 24185 1521620820
1 f none /usr/share/opensvc/lib/rcMountsDarwin.py 0664 root root 1341 28103 1521620820
1 f none /usr/share/opensvc/lib/rcMountsFreeBSD.py 0664 root root 1341 28103 1521620820
1 f none /usr/share/opensvc/lib/rcMountsHP-UX.py 0664 root root 1157 18837 1521620820
1 f none /usr/share/opensvc/lib/rcMountsLinux.py 0664 root root 1221 25481 1521620820
1 f none /usr/share/opensvc/lib/rcMountsOSF1.py 0664 root root 936 2616 1521620820
1 f none /usr/share/opensvc/lib/rcMountsSunOS.py 0664 root root 1141 16786 1521620820
1 f none /usr/share/opensvc/lib/rcMountsWindows.py 0664 root root 1733 3380 1521620820
1 f none /usr/share/opensvc/lib/rcNecIsm.py 0664 root root 11160 57005 1521620820
1 f none /usr/share/opensvc/lib/rcNetapp.py 0664 root root 2869 13635 1521620820
1 f none /usr/share/opensvc/lib/rcNexenta.py 0664 root root 11108 17875 1521620820
1 f none /usr/share/opensvc/lib/rcNode.py 0664 root root 1003 13252 1521620820
1 f none /usr/share/opensvc/lib/rcNsr.py 0664 root root 1179 17717 1521620820
1 f none /usr/share/opensvc/lib/rcOptParser.py 0664 root root 13908 7682 1521620820
1 f none /usr/share/opensvc/lib/rcOs.py 0664 root root 128 9832 1521620820
1 f none /usr/share/opensvc/lib/rcOsHP-UX.py 0664 root root 122 10053 1521620820
1 f none /usr/share/opensvc/lib/rcOsLinux.py 0664 root root 260 19577 1521620820
1 f none /usr/share/opensvc/lib/rcOsSunOS.py 0664 root root 177 13842 1521620820
1 f none /usr/share/opensvc/lib/rcOvm.py 0664 root root 3600 1558 1521620820
1 f none /usr/share/opensvc/lib/rcPasswdAIX.py 0664 root root 347 27171 1521620820
1 f none /usr/share/opensvc/lib/rcPasswdFreeBSD.py 0664 root root 331 25913 1521620820
1 f none /usr/share/opensvc/lib/rcPasswdLinux.py 0664 root root 353 27529 1521620820
1 f none /usr/share/opensvc/lib/rcPasswdSunOS.py 0664 root root 1970 12852 1521620820
1 f none /usr/share/opensvc/lib/rcPgLinux.py 0664 root root 12055 63594 1521620820
1 f none /usr/share/opensvc/lib/rcPkgAIX.py 0664 root root 508 37629 1521620820
1 f none /usr/share/opensvc/lib/rcPkgDarwin.py 0664 root root 993 10719 1521620820
1 f none /usr/share/opensvc/lib/rcPkgFreeBSD.py 0664 root root 1019 10121 1521620820
1 f none /usr/share/opensvc/lib/rcPkgHP-UX.py 0664 root root 824 59934 1521620820
1 f none /usr/share/opensvc/lib/rcPkgLinux.py 0664 root root 1690 59442 1521620820
1 f none /usr/share/opensvc/lib/rcPkgOSF1.py 0664 root root 1310 29504 1521620820
1 f none /usr/share/opensvc/lib/rcPkgSunOS.py 0664 root root 2818 3579 1521620820
1 f none /usr/share/opensvc/lib/rcPkgWindows.py 0664 root root 4532 13645 1521620820
1 f none /usr/share/opensvc/lib/rcPrintTable.py 0664 root root 4368 46202 1521620820
1 f none /usr/share/opensvc/lib/rcScheduler.py 0664 root root 45441 27303 1521620820
1 f none /usr/share/opensvc/lib/rcStats.py 0664 root root 3416 58275 1521620820
1 f none /usr/share/opensvc/lib/rcStatsAIX.py 0664 root root 4086 34947 1521620820
1 f none /usr/share/opensvc/lib/rcStatsCollectDarwin.py 0664 root root 2733 47042 1521620820
1 f none /usr/share/opensvc/lib/rcStatsCollectHP-UX.py 0664 root root 3564 44830 1521620820
1 f none /usr/share/opensvc/lib/rcStatsCollectLinux.py 0664 root root 5136 20462 1521620820
1 f none /usr/share/opensvc/lib/rcStatsCollectSunOS.py 0664 root root 6090 7875 1521620820
1 f none /usr/share/opensvc/lib/rcStatsDarwin.py 0664 root root 4704 38226 1521620820
1 f none /usr/share/opensvc/lib/rcStatsFreeBSD.py 0664 root root 4429 15820 1521620820
1 f none /usr/share/opensvc/lib/rcStatsHP-UX.py 0664 root root 6813 63717 1521620820
1 f none /usr/share/opensvc/lib/rcStatsLinux.py 0664 root root 13765 33604 1521620820
1 f none /usr/share/opensvc/lib/rcStatsSunOS.py 0664 root root 7372 5685 1521620820
1 f none /usr/share/opensvc/lib/rcStatus.py 0664 root root 6073 62071 1521620820
1 f none /usr/share/opensvc/lib/rcSymmetrix.py 0664 root root 40733 62639 1521620820
1 f none /usr/share/opensvc/lib/rcSysReport.py 0664 root root 13640 59632 1521620820
1 f none /usr/share/opensvc/lib/rcSysReportAIX.py 0664 root root 152 13334 1521620820
1 f none /usr/share/opensvc/lib/rcSysReportDarwin.py 0664 root root 152 13334 1521620820
1 f none /usr/share/opensvc/lib/rcSysReportFreeBSD.py 0664 root root 152 13334 1521620820
1 f none /usr/share/opensvc/lib/rcSysReportHP-UX.py 0664 root root 152 13334 1521620820
1 f none /usr/share/opensvc/lib/rcSysReportLinux.py 0664 root root 152 13334 1521620820
1 f none /usr/share/opensvc/lib/rcSysReportOSF1.py 0664 root root 152 13334 1521620820
1 f none /usr/share/opensvc/lib/rcSysReportSunOS.py 0664 root root 152 13334 1521620820
1 f none /usr/share/opensvc/lib/rcUpdatePkgAIX.py 0664 root root 223 17362 1521620820
1 f none /usr/share/opensvc/lib/rcUpdatePkgDarwin.py 0664 root root 448 35295 1521620820
1 f none /usr/share/opensvc/lib/rcUpdatePkgHP-UX.py 0664 root root 264 21363 1521620820
1 f none /usr/share/opensvc/lib/rcUpdatePkgLinux.py 0664 root root 578 45763 1521620820
1 f none /usr/share/opensvc/lib/rcUpdatePkgOSF1.py 0664 root root 685 52234 1521620820
1 f none /usr/share/opensvc/lib/rcUpdatePkgSunOS.py 0664 root root 1626 58834 1521620820
1 f none /usr/share/opensvc/lib/rcUpdatePkgWindows.py 0664 root root 178 13967 1521620820
1 f none /usr/share/opensvc/lib/rcUtilities.py 0664 root root 30779 33797 1521620820
1 f none /usr/share/opensvc/lib/rcUtilitiesAIX.py 0664 root root 379 28131 1521620820
1 f none /usr/share/opensvc/lib/rcUtilitiesDarwin.py 0664 root root 429 30256 1521620820
1 f none /usr/share/opensvc/lib/rcUtilitiesFreeBSD.py 0664 root root 346 25966 1521620820
1 f none /usr/share/opensvc/lib/rcUtilitiesHP-UX.py 0664 root root 315 23090 1521620820
1 f none /usr/share/opensvc/lib/rcUtilitiesLinux.py 0664 root root 7407 33255 1521620820
1 f none /usr/share/opensvc/lib/rcUtilitiesOSF1.py 0664 root root 315 23086 1521620820
1 f none /usr/share/opensvc/lib/rcUtilitiesSunOS.py 0664 root root 1325 29989 1521620820
1 f none /usr/share/opensvc/lib/rcUtilitiesWindows.py 0664 root root 826 150 1521620820
1 f none /usr/share/opensvc/lib/rcVioServer.py 0664 root root 3310 51262 1521620820
1 f none /usr/share/opensvc/lib/rcWakeOnLan.py 0664 root root 1425 40136 1521620820
1 f none /usr/share/opensvc/lib/rcXtremio.py 0664 root root 26731 58450 1521620820
1 f none /usr/share/opensvc/lib/rcZfs.py 0664 root root 6598 21656 1521620820
1 f none /usr/share/opensvc/lib/rcZone.py 0664 root root 3215 47715 1521620820
1 f none /usr/share/opensvc/lib/resApp.py 0664 root root 16349 22896 1521620820
1 f none /usr/share/opensvc/lib/resAppVm.py 0664 root root 3241 42280 1521620820
1 f none /usr/share/opensvc/lib/resContainer.py 0664 root root 8332 20177 1521620820
1 f none /usr/share/opensvc/lib/resContainerAmazon.py 0664 root root 9682 32993 1521620820
1 f none /usr/share/opensvc/lib/resContainerDocker.py 0664 root root 21963 50349 1521620820
1 f none /usr/share/opensvc/lib/resContainerEsx.py 0664 root root 3551 50279 1521620820
1 f none /usr/share/opensvc/lib/resContainerHpVm.py 0664 root root 4044 27602 1521620820
1 f none /usr/share/opensvc/lib/resContainerJail.py 0664 root root 3361 37986 1521620820
1 f none /usr/share/opensvc/lib/resContainerKvm.py 0664 root root 5650 13677 1521620820
1 f none /usr/share/opensvc/lib/resContainerLdom.py 0664 root root 4124 22970 1521620820
1 f none /usr/share/opensvc/lib/resContainerLxc.py 0664 root root 14804 64835 1521620820
1 f none /usr/share/opensvc/lib/resContainerLxd.py 0664 root root 8065 5666 1521620820
1 f none /usr/share/opensvc/lib/resContainerOpenstack.py 0664 root root 10775 50515 1521620820
1 f none /usr/share/opensvc/lib/resContainerOvm.py 0664 root root 5002 23092 1521620820
1 f none /usr/share/opensvc/lib/resContainerSrp.py 0664 root root 7941 22296 1521620820
1 f none /usr/share/opensvc/lib/resContainerVbox.py 0664 root root 4052 23361 1521620820
1 f none /usr/share/opensvc/lib/resContainerVcloud.py 0664 root root 8896 59237 1521620820
1 f none /usr/share/opensvc/lib/resContainerVz.py 0664 root root 4828 5126 1521620820
1 f none /usr/share/opensvc/lib/resContainerXen.py 0664 root root 3454 49898 1521620820
1 f none /usr/share/opensvc/lib/resContainerZone.py 0664 root root 17334 64680 1521620820
1 f none /usr/share/opensvc/lib/resDisk.py 0664 root root 1760 507 1521620820
1 f none /usr/share/opensvc/lib/resDiskAmazon.py 0664 root root 7662 40696 1521620820
1 f none /usr/share/opensvc/lib/resDiskDisk.py 0664 root root 3964 16011 1521620820
1 f none /usr/share/opensvc/lib/resDiskDiskLinux.py 0664 root root 1424 44806 1521620820
1 f none /usr/share/opensvc/lib/resDiskDrbd.py 0664 root root 6379 61839 1521620820
1 f none /usr/share/opensvc/lib/resDiskGandi.py 0664 root root 6117 39737 1521620820
1 f none /usr/share/opensvc/lib/resDiskGce.py 0664 root root 5695 25739 1521620820
1 f none /usr/share/opensvc/lib/resDiskHpVm.py 0664 root root 4411 41130 1521620820
1 f none /usr/share/opensvc/lib/resDiskLdom.py 0664 root root 3439 29084 1521620820
1 f none /usr/share/opensvc/lib/resDiskLoop.py 0664 root root 568 43300 1521620820
1 f none /usr/share/opensvc/lib/resDiskLoopDarwin.py 0664 root root 1535 50915 1521620820
1 f none /usr/share/opensvc/lib/resDiskLoopFreeBSD.py 0664 root root 2162 27811 1521620820
1 f none /usr/share/opensvc/lib/resDiskLoopLinux.py 0664 root root 2336 47616 1521620820
1 f none /usr/share/opensvc/lib/resDiskLoopSunOS.py 0664 root root 552 41762 1521620820
1 f none /usr/share/opensvc/lib/resDiskLvLinux.py 0664 root root 4041 29295 1521620820
1 f none /usr/share/opensvc/lib/resDiskMdLinux.py 0664 root root 11846 445 1521620820
1 f none /usr/share/opensvc/lib/resDiskRadosLinux.py 0664 root root 8198 53444 1521620820
1 f none /usr/share/opensvc/lib/resDiskRaw.py 0664 root root 12698 56686 1521620820
1 f none /usr/share/opensvc/lib/resDiskRawHP-UX.py 0664 root root 856 54330 1521620820
1 f none /usr/share/opensvc/lib/resDiskRawLinux.py 0664 root root 9013 45745 1521620820
1 f none /usr/share/opensvc/lib/resDiskRawSunOS.py 0664 root root 939 64865 1521620820
1 f none /usr/share/opensvc/lib/resDiskVdisk.py 0664 root root 1835 842 1521620820
1 f none /usr/share/opensvc/lib/resDiskVgAIX.py 0664 root root 8377 58508 1521620820
1 f none /usr/share/opensvc/lib/resDiskVgAdvfs.py 0664 root root 2900 14664 1521620820
1 f none /usr/share/opensvc/lib/resDiskVgHP-UX.py 0664 root root 11604 33204 1521620820
1 f none /usr/share/opensvc/lib/resDiskVgLinux.py 0664 root root 8597 25624 1521620820
1 f none /usr/share/opensvc/lib/resDiskVgVeritas.py 0664 root root 3166 16531 1521620820
1 f none /usr/share/opensvc/lib/resDiskZfs.py 0664 root root 7181 31026 1521620820
1 f none /usr/share/opensvc/lib/resFs.py 0664 root root 7497 23131 1521620820
1 f none /usr/share/opensvc/lib/resFsAIX.py 0664 root root 4543 60455 1521620820
1 f none /usr/share/opensvc/lib/resFsDarwin.py 0664 root root 6914 29838 1521620820
1 f none /usr/share/opensvc/lib/resFsDir.py 0664 root root 3798 13359 1521620820
1 f none /usr/share/opensvc/lib/resFsDocker.py 0664 root root 1831 674 1521620820
1 f none /usr/share/opensvc/lib/resFsFreeBSD.py 0664 root root 4848 18489 1521620820
1 f none /usr/share/opensvc/lib/resFsHP-UX.py 0664 root root 3139 29012 1521620820
1 f none /usr/share/opensvc/lib/resFsLinux.py 0664 root root 20092 8427 1521620820
1 f none /usr/share/opensvc/lib/resFsOSF1.py 0664 root root 3954 25417 1521620820
1 f none /usr/share/opensvc/lib/resFsSunOS.py 0664 root root 6151 37274 1521620820
1 f none /usr/share/opensvc/lib/resFsWindows.py 0664 root root 4573 37493 1521620820
1 f none /usr/share/opensvc/lib/resIp.py 0664 root root 20551 41057 1521620820
1 f none /usr/share/opensvc/lib/resIpAIX.py 0664 root root 904 5802 1521620820
1 f none /usr/share/opensvc/lib/resIpAmazon.py 0664 root root 4275 53335 1521620820
1 f none /usr/share/opensvc/lib/resIpCni.py 0664 root root 12929 32369 1521620820
1 f none /usr/share/opensvc/lib/resIpCrossbow.py 0664 root root 5324 51473 1521620820
1 f none /usr/share/opensvc/lib/resIpDarwin.py 0664 root root 901 4974 1521620820
1 f none /usr/share/opensvc/lib/resIpDockerLinux.py 0664 root root 13744 38754 1521620820
1 f none /usr/share/opensvc/lib/resIpFreeBSD.py 0664 root root 920 6901 1521620820
1 f none /usr/share/opensvc/lib/resIpGce.py 0664 root root 6241 5502 1521620820
1 f none /usr/share/opensvc/lib/resIpHP-UX.py 0664 root root 1112 18823 1521620820
1 f none /usr/share/opensvc/lib/resIpLinux.py 0664 root root 1732 1951 1521620820
1 f none /usr/share/opensvc/lib/resIpOSF1.py 0664 root root 919 6744 1521620820
1 f none /usr/share/opensvc/lib/resIpSunOS.py 0664 root root 784 61725 1521620820
1 f none /usr/share/opensvc/lib/resIpWindows.py 0664 root root 2573 8432 1521620820
1 f none /usr/share/opensvc/lib/resIpZone.py 0664 root root 2110 17157 1521620820
1 f none /usr/share/opensvc/lib/resScsiReserv.py 0664 root root 8833 47940 1521620820
1 f none /usr/share/opensvc/lib/resScsiReservFreeBSD.py 0664 root root 257 21347 1521620820
1 f none /usr/share/opensvc/lib/resScsiReservHP-UX.py 0664 root root 5032 39678 1521620820
1 f none /usr/share/opensvc/lib/resScsiReservLinux.py 0664 root root 257 21347 1521620820
1 f none /usr/share/opensvc/lib/resScsiReservOSF1.py 0664 root root 6467 91 1521620820
1 f none /usr/share/opensvc/lib/resScsiReservSg.py 0664 root root 6006 57709 1521620820
1 f none /usr/share/opensvc/lib/resScsiReservSunOS.py 0664 root root 79 7221 1521620820
1 f none /usr/share/opensvc/lib/resShareNfsHP-UX.py 0664 root root 3662 64679 1521620820
1 f none /usr/share/opensvc/lib/resShareNfsLinux.py 0664 root root 4404 58192 1521620820
1 f none /usr/share/opensvc/lib/resShareNfsSunOS.py 0664 root root 3442 49166 1521620820
1 f none /usr/share/opensvc/lib/resSync.py 0664 root root 7355 24451 1521620820
1 f none /usr/share/opensvc/lib/resSyncBtrfs.py 0664 root root 15813 3990 1521620820
1 f none /usr/share/opensvc/lib/resSyncBtrfsSnap.py 0664 root root 6730 25862 1521620820
1 f none /usr/share/opensvc/lib/resSyncDcs.py 0664 root root 4230 57331 1521620820
1 f none /usr/share/opensvc/lib/resSyncDcsCkpt.py 0664 root root 6743 37423 1521620820
1 f none /usr/share/opensvc/lib/resSyncDcsSnap.py 0664 root root 6232 36256 1521620820
1 f none /usr/share/opensvc/lib/resSyncDds.py 0664 root root 13825 24061 1521620820
1 f none /usr/share/opensvc/lib/resSyncDocker.py 0664 root root 5518 9868 1521620820
1 f none /usr/share/opensvc/lib/resSyncEvasnap.py 0664 root root 10712 63857 1521620820
1 f none /usr/share/opensvc/lib/resSyncHp3par.py 0664 root root 10026 19464 1521620820
1 f none /usr/share/opensvc/lib/resSyncHp3parSnap.py 0664 root root 3373 54925 1521620820
1 f none /usr/share/opensvc/lib/resSyncIbmdsSnap.py 0664 root root 8639 25195 1521620820
1 f none /usr/share/opensvc/lib/resSyncNecIsmSnap.py 0664 root root 5722 16049 1521620820
1 f none /usr/share/opensvc/lib/resSyncNetapp.py 0664 root root 10144 16124 1521620820
1 f none /usr/share/opensvc/lib/resSyncNexenta.py 0664 root root 8292 13328 1521620820
1 f none /usr/share/opensvc/lib/resSyncRados.py 0664 root root 10473 12883 1521620820
1 f none /usr/share/opensvc/lib/resSyncRsync.py 0664 root root 16522 34320 1521620820
1 f none /usr/share/opensvc/lib/resSyncS3.py 0664 root root 9315 8609 1521620820
1 f none /usr/share/opensvc/lib/resSyncSymSrdfS.py 0664 root root 21519 42070 1521620820
1 f none /usr/share/opensvc/lib/resSyncSymclone.py 0664 root root 10364 45748 1521620820
1 f none /usr/share/opensvc/lib/resSyncSymcloneLinux.py 0664 root root 2473 44536 1521620820
1 f none /usr/share/opensvc/lib/resSyncZfs.py 0664 root root 15088 16962 1521620820
1 f none /usr/share/opensvc/lib/resSyncZfsSnap.py 0664 root root 5664 12945 1521620820
1 f none /usr/share/opensvc/lib/resTask.py 0664 root root 3692 18467 1521620820
1 f none /usr/share/opensvc/lib/resources.py 0664 root root 35737 2654 1521620820
1 f none /usr/share/opensvc/lib/resourceset.py 0664 root root 13352 54205 1521620820
1 f none /usr/share/opensvc/lib/snap.py 0664 root root 4322 55486 1521620820
1 f none /usr/share/opensvc/lib/snapAdvfsOSF1.py 0664 root root 3209 48800 1521620820
1 f none /usr/share/opensvc/lib/snapJfs2AIX.py 0664 root root 4107 32926 1521620820
1 f none /usr/share/opensvc/lib/snapLvmLinux.py 0664 root root 4504 64968 1521620820
1 f none /usr/share/opensvc/lib/snapVxfsHP-UX.py 0664 root root 2749 6374 1521620820
1 f none /usr/share/opensvc/lib/snapZfsSunOS.py 0664 root root 1445 47441 1521620820
1 f none /usr/share/opensvc/lib/svc.py 0664 root root 173742 25578 1521620820
1 f none /usr/share/opensvc/lib/svcBuilder.py 0664 root root 66934 49971 1521620820
1 f none /usr/share/opensvc/lib/svcdict.py 0664 root root 184494 36255 1521620820
1 f none /usr/share/opensvc/lib/svcmgr.py 0664 root root 9565 24279 1521620820
1 f none /usr/share/opensvc/lib/svcmgr_parser.py 0664 root root 46264 12361 1521620820
1 f none /usr/share/opensvc/lib/svcmon.py 0664 root root 3860 26658 1521620820
1 f none /usr/share/opensvc/lib/tabulate.py 0664 root root 26534 22047 1521620820
1 f none /usr/share/opensvc/lib/version.py 0644 root root 21 1388 1521621049
1 f none /usr/share/opensvc/lib/wmi.py 0664 root root 46934 3064 1521620820
1 f none /usr/share/opensvc/lib/xmlrpcClient.py 0664 root root 46786 59025 1521620820
1 d none /var/lib/opensvc 0755 root root
1 d none /var/lib/opensvc/cache 0755 root root
1 d none /var/lib/opensvc/compliance 0755 root root
1 d none /var/lib/opensvc/compliance/com.opensvc 0755 root root
1 f none /var/lib/opensvc/compliance/com.opensvc/ansible_playbook.py 0775 root root 5285 5233 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/authkey.py 0775 root root 17241 7192 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/bios.py 0775 root root 2165 27446 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/chkconfig.py 0775 root root 3667 1530 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/comp.py 0664 root root 15844 35528 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/cron.py 0775 root root 6862 10618 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/etcsystem.py 0775 root root 6722 9689 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/file.py 0775 root root 12408 50056 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/fileinc.py 0775 root root 8911 17597 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/fileprop.py 0775 root root 9631 27377 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/firmware.py 0775 root root 6707 19567 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/fs.py 0775 root root 22529 37954 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/group.py 0775 root root 7738 35340 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/group_membership.py 0775 root root 7892 62851 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/keyval.py 0775 root root 11686 35293 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/keyval_parser.py 0775 root root 6162 41502 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/linux.mpath.py 0775 root root 15677 40723 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/nodeconf.py 0775 root root 5929 23642 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/package.py 0775 root root 26262 61773 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/process.py 0775 root root 13316 54272 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/rc.py 0775 root root 4513 59049 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/remove_files.py 0775 root root 2367 47306 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/self.signed.cert.py 0775 root root 5369 59062 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/smfcfgs.py 0775 root root 14757 64492 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/sudoers.py 0775 root root 2376 45343 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/svcconf.py 0775 root root 9997 50975 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/symlink.py 0775 root root 3817 27588 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/sysctl.py 0775 root root 8354 6449 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/sysvinit.py 0775 root root 7817 31424 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/timedatectl.py 0775 root root 5731 16072 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/user.py 0775 root root 16544 63021 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/utilities.py 0664 root root 752 62301 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/volume_file.py 0775 root root 2988 45078 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/vuln.py 0775 root root 23120 7213 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/xinetd.py 0775 root root 5514 57669 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/yes 0775 root root 121 9914 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/zfs.py 0775 root root 8814 16796 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/zpool.py 0775 root root 5546 35703 1521620820
1 f none /var/lib/opensvc/compliance/com.opensvc/zprop.py 0775 root root 4413 47974 1521620820
1 d none /var/log 0755 root sys
1 d none /var/log/opensvc 0755 root root
1 i pkginfo 211 17697 1521621050
1 i postinstall 671 61368 1521621049
1 i preinstall 1211 34464 1521621049
1 i preremove 403 31121 1521621049
  0707010005ca52000041ed0000000000000000000000025ab2183a00000000000000660000000000000000000000000000000800000000install   0707010005ca53000081a40000000000000000000000015ab218390000029f000000660000000000000000000000000000001400000000install/postinstall   #!/sbin/sh

su root -c "PATH=${PKG_INSTALL_ROOT}/usr/local/bin:/usr/local/bin:/opt/opensvc/bin/pkg:/.users/mount/averon/bin:/.users/mount/averon/scripts:/.users/mount/averon/bin:/.users/mount/averon/scripts:/usr/sbin:/usr/bin:/opt/csw/bin:/opt/opensvc/bin:/bin:/usr/ccs/bin:/opt/sfw/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/etc:/usr/dt/bin:/usr/ucb:/usr/symcli/bin:/opt/Navisphere/bin:/sbin:/usr/sbin:/bin:/usr/ccs/bin:/opt/sfw/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/etc:/usr/dt/bin:/usr/ucb:/usr/symcli/bin:/opt/Navisphere/bin:/sbin:/usr/sbin:/opt/sfw/bin:/usr/sfw/bin:/usr/local/bin ${PKG_INSTALL_ROOT}//usr/share/opensvc/bin/postinstall"
 0707010005ca56000081ed0000000000000000000000015ab21839000004bb000000660000000000000000000000000000001300000000install/preinstall    #!/bin/sh

# set OSVC_ROOT_PATH
test -f /etc/sysconfig/opensvc && . /etc/sysconfig/opensvc
test -f /etc/default/opensvc && . /etc/default/opensvc
test -f /etc/defaults/opensvc && . /etc/defaults/opensvc

if [ -z "$OSVC_ROOT_PATH" ]
then
	FLAG=/var/lib/opensvc/postinstall.restart
else
	FLAG=$OSVC_ROOT_PATH/var/postinstall.restart
	PATH=$PATH:$OSVC_ROOT_PATH/bin
	export PATH
fi

# create the flag hosting dir if it does not exist yet
mkdir -p `dirname $FLAG` >/dev/null 2>&1

# clean up lingering restart flag
rm -f $FLAG

# opensvc was not installed yet. just set the daemon start flag 
which nodemgr >/dev/null 2>&1 || {
	touch $FLAG
	exit 0
}

nodemgr daemon running >/dev/null 2>&1
RUNNING=$?

# option not found => upgrade from 1.8-
if [ $RUNNING -eq 2 ]
then
	echo "daemon flagged for start"
	touch $FLAG

	echo "freeze all services"
	svcmgr freeze >/dev/null 2>&1
        # can output error if no services are present
	exit 0
fi

# set a flag for postinstall to know if the daemon should be restarted
if [ $RUNNING -eq 0 ]
then
	echo "daemon flagged for restart"
	touch $FLAG

        echo "stop the daemon, leaving the node in agent upgrade mode"
        OPENSVC_AGENT_UPGRADE=1 nodemgr daemon stop
fi
 0707010005ca57000081ed0000000000000000000000015ab2183900000193000000660000000000000000000000000000001200000000install/preremove #!/bin/sh

ARGS=$1

if [ `uname` -eq 'SunOS' ]
then
	ARGS=0
fi

if [ -z "$OSVC_ROOT_PATH" ]
then
	FLAG=/var/lib/opensvc/postinstall.restart
else
	FLAG=$OSVC_ROOT_PATH/var/postinstall.restart
	PATH=$PATH:$OSVC_ROOT_PATH/bin
	export PATH
fi

nodemgr daemon running >/dev/null 2>&1
RUNNING=$?

if [ $ARGS -eq 0 ]
then
	# uninstall package
	if [ $RUNNING -eq 0 ]
	then
		nodemgr daemon stop
	fi
	exit 0
fi

 0707010005c704000041ed0000000000000000000000055ab2183a00000000000000660000000000000000000000000000000500000000root  0707010005c705000041ed0000000000000000000000035ab2183a00000000000000660000000000000000000000000000000900000000root/etc  0707010005c706000041ed0000000000000000000000025ab2183a00000000000000660000000000000000000000000000001b00000000root/etc/bash_completion.d    0707010005c707000081b40000000000000000000000015ab217540000763e000000660000000000000000000000000000002300000000root/etc/bash_completion.d/opensvc    

svcmgr="abort clear collector compliance create delete disable dns docker edit enable freeze frozen get giveback logs ls migrate pg postsync presync print provision prstatus pull push resource restart resync run scale set shutdown start startstandby status stop switch sync takeover thaw toc unprovision unset update validate"
svcmgr_collector="ack alerts asset checks create disks events list log networks show tag untag"
svcmgr_collector_ack="action unavailability"
svcmgr_collector_create="tag"
svcmgr_collector_list="actions tags unavailability"
svcmgr_collector_list_unavailability="ack"
svcmgr_collector_show="actions tags"
svcmgr_compliance="attach auto check detach env fix fixable list show"
svcmgr_compliance_list="moduleset ruleset"
svcmgr_compliance_show="moduleset ruleset status"
svcmgr_dns="update"
svcmgr_edit="config"
svcmgr_pg="freeze kill thaw"
svcmgr_print="base config devs exposed resinfo resource schedule status sub"
svcmgr_print_base="devs"
svcmgr_print_config="mtime"
svcmgr_print_exposed="devs"
svcmgr_print_resource="status"
svcmgr_print_sub="devs"
svcmgr_push="config encap resinfo"
svcmgr_push_encap="config"
svcmgr_resource="monitor"
svcmgr_set="provisioned unprovisioned"
svcmgr_sync="all break drp establish full nodes quiesce restore resume resync revert split update verify"
svcmgr_validate="config"
svcmgr_update="--color --daemon --debug --disable-rollback --dry-run --env --local --master --node --provision --resource --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_set="--add --color --daemon --debug --dry-run --env --index --kw --local --master --node --param --remove --rid --slave --slaves --status --subsets --tags --value --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_unset="--color --daemon --debug --dry-run --env --kw --local --master --node --param --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_get="--color --daemon --debug --dry-run --env --eval --impersonate --kw --local --master --node --param --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_edit_config="--color --daemon --debug --discard --env --recover --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_delete="--color --daemon --debug --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --time --unprovision --wait --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_validate_config="--color --daemon --debug --env --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_create="--color --config --daemon --debug --disable-rollback --dry-run --env --local --master --node --provision --resource --rid --slave --slaves --status --subsets --tags --template --waitlock -c --cluster -f --force -h --help -i --interactive -p --parallel -s --service"
svcmgr_print_config="--color --daemon --debug --env --eval --format --impersonate --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_print_resinfo="--color --daemon --debug --env --format --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_compliance_fix="--attach --color --daemon --debug --env --module --moduleset --ruleset-date --status --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_compliance_list_moduleset="--color --daemon --debug --env --moduleset --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_compliance_detach="--color --daemon --debug --env --moduleset --ruleset --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_compliance_attach="--color --daemon --debug --env --moduleset --ruleset --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_compliance_show_ruleset="--color --daemon --debug --env --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_compliance_env="--color --daemon --debug --env --module --moduleset --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_compliance_show_status="--color --daemon --debug --env --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_compliance_list_ruleset="--color --daemon --debug --env --ruleset --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_compliance_fixable="--attach --color --daemon --debug --env --module --moduleset --ruleset-date --status --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_compliance_show_moduleset="--color --daemon --debug --env --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_compliance_check="--attach --color --daemon --debug --env --module --moduleset --ruleset-date --status --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_compliance_auto="--attach --color --cron --daemon --debug --env --module --moduleset --ruleset-date --status --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_collector_disks="--color --daemon --debug --env --format --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_collector_log="--color --daemon --debug --env --message --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_collector_networks="--color --daemon --debug --env --format --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_collector_list_actions="--begin --color --daemon --debug --end --env --format --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_collector_checks="--color --daemon --debug --env --format --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_collector_tag="--color --daemon --debug --env --status --tag --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_collector_ack_unavailability="--account --author --begin --color --comment --daemon --debug --duration --end --env --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_collector_list_tags="--color --daemon --debug --env --format --like --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_collector_untag="--color --daemon --debug --env --status --tag --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_collector_show_actions="--begin --color --daemon --debug --end --env --format --id --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_collector_show_tags="--color --daemon --debug --env --format --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_collector_list_unavailability_ack="--author --begin --color --comment --daemon --debug --end --env --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_collector_create_tag="--color --daemon --debug --env --status --tag --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_collector_ack_action="--author --color --comment --daemon --debug --env --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_collector_alerts="--color --daemon --debug --env --format --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_collector_events="--begin --color --daemon --debug --end --env --format --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_collector_asset="--color --daemon --debug --env --format --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_takeover="--color --daemon --debug --disable-rollback --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --time --wait --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_sync_update="--color --daemon --debug --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_push_encap_config="--color --cron --daemon --debug --env --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_shutdown="--color --daemon --debug --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --time --wait --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_push_resinfo="--color --cron --daemon --debug --env --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_sync_establish="--color --daemon --debug --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_start="--color --daemon --debug --disable-rollback --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --time --upto --wait --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_ls="--color --daemon --debug --env --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_toc="--color --daemon --debug --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_set_provisioned="--color --daemon --debug --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_print_status="--color --daemon --debug --env --format --hide-disabled --show-disabled --status --waitlock -c --cluster -h --help -p --parallel -r --refresh -s --service"
svcmgr_enable="--color --daemon --debug --env --rid --status --subsets --tags --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_thaw="--color --daemon --debug --env --local --master --node --slave --slaves --status --time --wait --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_stop="--color --daemon --debug --downto --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --time --wait --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_print_exposed_devs="--color --daemon --debug --env --format --rid --status --subsets --tags --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_unprovision="--color --daemon --debug --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --time --wait --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_print_devs="--color --daemon --debug --env --format --rid --status --subsets --tags --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_provision="--color --daemon --debug --disable-rollback --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --time --wait --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_restart="--color --daemon --debug --disable-rollback --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_set_unprovisioned="--color --daemon --debug --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_pull="--color --daemon --debug --disable-rollback --env --provision --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_sync_all="--color --cron --daemon --debug --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_frozen="--color --daemon --debug --env --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_sync_quiesce="--color --daemon --debug --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_sync_nodes="--color --daemon --debug --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_print_config_mtime="--color --daemon --debug --env --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_print_sub_devs="--color --daemon --debug --env --format --rid --status --subsets --tags --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_clear="--color --daemon --debug --env --local --node --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_prstatus="--color --daemon --debug --env --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_logs="--backlog --color --daemon --debug --env --follow --local --no-pager --node --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_print_base_devs="--color --daemon --debug --env --format --rid --status --subsets --tags --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_migrate="--color --daemon --debug --disable-rollback --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --time --to --wait --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_sync_resync="--color --daemon --debug --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_freeze="--color --daemon --debug --env --local --master --node --slave --slaves --status --time --wait --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_abort="--color --daemon --debug --env --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_resync="--color --daemon --debug --disable-rollback --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_pg_freeze="--color --daemon --debug --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_scale="--color --daemon --debug --env --local --node --status --to --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_presync="--color --daemon --debug --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_sync_split="--color --daemon --debug --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_pg_kill="--color --daemon --debug --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_giveback="--color --daemon --debug --disable-rollback --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --time --wait --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_print_resource_status="--color --daemon --debug --env --format --rid --status --waitlock -c --cluster -h --help -p --parallel -r --refresh -s --service"
svcmgr_sync_break="--color --daemon --debug --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_status="--color --cron --daemon --debug --env --status --waitlock -c --cluster -h --help -p --parallel -r --refresh -s --service"
svcmgr_dns_update="--color --daemon --debug --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_sync_full="--color --daemon --debug --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_run="--color --cron --daemon --debug --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_resource_monitor="--color --cron --daemon --debug --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_pg_thaw="--color --daemon --debug --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_sync_restore="--color --daemon --debug --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_startstandby="--color --daemon --debug --disable-rollback --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_postsync="--color --daemon --debug --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_sync_resume="--color --daemon --debug --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_sync_revert="--color --daemon --debug --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_sync_drp="--color --daemon --debug --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_push_config="--color --cron --daemon --debug --env --status --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_sync_verify="--color --daemon --debug --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_switch="--color --daemon --debug --disable-rollback --dry-run --env --local --master --node --rid --slave --slaves --status --subsets --tags --time --to --wait --waitlock -c --cluster -f --force -h --help -p --parallel -s --service"
svcmgr_disable="--color --daemon --debug --env --rid --status --subsets --tags --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_print_schedule="--color --daemon --debug --env --format --status --verbose --waitlock -c --cluster -h --help -p --parallel -s --service"
svcmgr_docker="--color --daemon --debug --env --status --waitlock -c --cluster -h --help -p --parallel -s --service"
nodemgr="array auto checks collect collector compliance daemon dequeue discover edit events freeze frozen get logs network ping print prkey pushasset pushbrocade pushcentera pushdcs pushdisks pushemcvnx pusheva pushfreenas pushgcedisks pushhds pushhp3par pushibmds pushibmsvc pushnecism pushnetapp pushnsr pushpatch pushpkg pushstats pushsym pushvioserver pushxtremio reboot register rotate scanscsi schedule set shutdown stonith sysreport thaw unschedule unset updatecomp updatepkg validate wol"
nodemgr_auto="reboot"
nodemgr_collect="stats"
nodemgr_collector="ack alerts asset checks cli create disks events list log networks search show tag untag"
nodemgr_collector_ack="action"
nodemgr_collector_create="tag"
nodemgr_collector_list="actions filtersets nodes services tags"
nodemgr_collector_show="actions tags"
nodemgr_compliance="attach auto check detach env fix fixable list show"
nodemgr_compliance_list="module moduleset ruleset"
nodemgr_compliance_show="moduleset ruleset status"
nodemgr_daemon="blacklist join leave restart running shutdown start status stop"
nodemgr_daemon_blacklist="clear status"
nodemgr_dequeue="actions"
nodemgr_edit="authconfig config"
nodemgr_network="ls show"
nodemgr_print="authconfig config devs schedule"
nodemgr_rotate="root"
nodemgr_rotate_root="pw"
nodemgr_schedule="reboot"
nodemgr_schedule_reboot="status"
nodemgr_unschedule="reboot"
nodemgr_validate="config"
nodemgr_compliance_fix="--attach --color --debug --force --format --module --moduleset --ruleset-date -h --help"
nodemgr_compliance_list_moduleset="--color --debug --format --moduleset -h --help"
nodemgr_compliance_detach="--color --debug --format --moduleset --ruleset -h --help"
nodemgr_compliance_attach="--color --debug --format --moduleset --ruleset -h --help"
nodemgr_compliance_show_ruleset="--color --debug --format -h --help"
nodemgr_compliance_env="--color --debug --format --module --moduleset -h --help"
nodemgr_compliance_show_status="--color --debug --format -h --help"
nodemgr_compliance_list_ruleset="--color --debug --format --ruleset -h --help"
nodemgr_compliance_fixable="--attach --color --debug --force --format --module --moduleset --ruleset-date -h --help"
nodemgr_compliance_list_module="--color --debug --format -h --help"
nodemgr_compliance_show_moduleset="--color --debug --format -h --help"
nodemgr_compliance_check="--attach --color --debug --force --format --module --moduleset --ruleset-date -h --help"
nodemgr_compliance_auto="--color --cron --debug --format -h --help"
nodemgr_prkey="--color --debug --format -h --help"
nodemgr_pushnsr="--color --cron --debug --format --object -h --help"
nodemgr_pushcentera="--color --cron --debug --format --object -h --help"
nodemgr_pushibmds="--color --cron --debug --format --object -h --help"
nodemgr_pushhp3par="--color --cron --debug --format --object -h --help"
nodemgr_pushxtremio="--color --cron --debug --format --object -h --help"
nodemgr_pushpatch="--color --cron --debug --format -h --help"
nodemgr_pushpkg="--color --cron --debug --format -h --help"
nodemgr_pushvioserver="--color --cron --debug --format --object -h --help"
nodemgr_pushasset="--color --cron --debug --format --sync -h --help"
nodemgr_pushibmsvc="--color --cron --debug --format --object -h --help"
nodemgr_pushnetapp="--color --cron --debug --format --object -h --help"
nodemgr_pushgcedisks="--color --cron --debug --format --object -h --help"
nodemgr_pusheva="--color --cron --debug --format --object -h --help"
nodemgr_pushnecism="--color --cron --debug --format --object -h --help"
nodemgr_pushdcs="--color --cron --debug --format --object -h --help"
nodemgr_pushbrocade="--color --cron --debug --format --object -h --help"
nodemgr_sysreport="--color --cron --debug --format -h --help"
nodemgr_pushemcvnx="--color --cron --debug --format --object -h --help"
nodemgr_pushfreenas="--color --cron --debug --format --object -h --help"
nodemgr_pushhds="--color --cron --debug --format --object -h --help"
nodemgr_pushstats="--begin --color --cron --debug --end --format --stats-dir -h --help"
nodemgr_pushsym="--color --cron --debug --format --object --symcli-db-file -h --help"
nodemgr_checks="--color --cron --debug --format -h --help"
nodemgr_pushdisks="--color --cron --debug --format -h --help"
nodemgr_discover="--color --debug --format -h --help"
nodemgr_print_authconfig="--color --debug --format -h --help"
nodemgr_set="--add --color --debug --format --index --kw --param --remove --value -h --help"
nodemgr_unset="--color --debug --format --kw --param -h --help"
nodemgr_get="--color --debug --eval --format --kw --param -h --help"
nodemgr_validate_config="--color --debug --format -h --help"
nodemgr_edit_config="--color --debug --discard --format --recover -h --help"
nodemgr_register="--app --color --debug --format --password --user -h --help"
nodemgr_edit_authconfig="--color --debug --format -h --help"
nodemgr_print_config="--color --debug --format -h --help"
nodemgr_logs="--backlog --color --debug --follow --format --local --node -h --help"
nodemgr_auto_reboot="--color --cron --debug --format -h --help"
nodemgr_freeze="--color --debug --format --local --node --time --wait -h --help"
nodemgr_schedule_reboot="--color --debug --format -h --help"
nodemgr_shutdown="--color --debug --format --local --node -h --help"
nodemgr_array="--color --debug --format -h --help"
nodemgr_scanscsi="--color --debug --format --hba --lun --target -h --help"
nodemgr_unschedule_reboot="--color --debug --format -h --help"
nodemgr_ping="--color --debug --format --node -h --help"
nodemgr_reboot="--color --debug --format --local --node -h --help"
nodemgr_wol="--broadcast --color --debug --format --mac --port -h --help"
nodemgr_collect_stats="--color --cron --debug --format -h --help"
nodemgr_events="--color --debug --format --local --node -h --help"
nodemgr_thaw="--color --debug --format --local --node --time --wait -h --help"
nodemgr_schedule_reboot_status="--color --debug --format -h --help"
nodemgr_print_devs="--color --debug --dev --format --reverse --verbose -h --help"
nodemgr_dequeue_actions="--color --cron --debug --format -h --help"
nodemgr_updatepkg="--color --debug --format --local --node -h --help"
nodemgr_frozen="--color --debug --format -h --help"
nodemgr_stonith="--color --debug --format --node -h --help"
nodemgr_rotate_root_pw="--color --cron --debug --format -h --help"
nodemgr_updatecomp="--color --debug --format --local --node -h --help"
nodemgr_print_schedule="--color --debug --format --verbose -h --help"
nodemgr_daemon_stop="--color --debug --format --local --node --thread-id -h --help"
nodemgr_daemon_status="--color --debug --format --local --node -h --help"
nodemgr_daemon_running="--color --debug --format -h --help"
nodemgr_daemon_leave="--color --debug --format -h --help"
nodemgr_daemon_blacklist_clear="--color --debug --format -h --help"
nodemgr_daemon_join="--color --debug --format --local --node --secret -h --help"
nodemgr_daemon_blacklist_status="--color --debug --format -h --help"
nodemgr_daemon_shutdown="--color --debug --format -h --help"
nodemgr_daemon_restart="--color --debug --format -h --help"
nodemgr_daemon_start="--color --debug --format --local --node --thread-id -h --help"
nodemgr_network_ls="--color --debug --format -h --help"
nodemgr_network_show="--color --debug --format --id -h --help"
nodemgr_collector_list_tags="--color --debug --format --like -h --help"
nodemgr_collector_disks="--color --debug --format -h --help"
nodemgr_collector_log="--color --debug --format --message -h --help"
nodemgr_collector_events="--begin --color --debug --end --format -h --help"
nodemgr_collector_search="--color --debug --format --like -h --help"
nodemgr_collector_list_filtersets="--color --debug --format -h --help"
nodemgr_collector_list_actions="--begin --color --debug --end --format -h --help"
nodemgr_collector_cli="--api --color --config --debug --format --insecure --password --refresh-api --save --user -h --help"
nodemgr_collector_tag="--color --debug --format --tag -h --help"
nodemgr_collector_list_services="--color --debug --format -h --help"
nodemgr_collector_networks="--color --debug --format -h --help"
nodemgr_collector_untag="--color --debug --format -h --help"
nodemgr_collector_show_actions="--begin --color --debug --end --format --id -h --help"
nodemgr_collector_checks="--color --debug --format -h --help"
nodemgr_collector_show_tags="--color --debug --format -h --help"
nodemgr_collector_create_tag="--color --debug --format --tag -h --help"
nodemgr_collector_list_nodes="--color --debug --format -h --help"
nodemgr_collector_ack_action="--author --color --comment --debug --format -h --help"
nodemgr_collector_alerts="--color --debug --format -h --help"
nodemgr_collector_asset="--color --debug --format -h --help"

opts_with_arg=( "--add" "--api" "--app" "--author" "--backlog" "--begin" "--broadcast" "--color" "--comment" "--config" "--dev" "--downto" "--duration" "--end" "--env" "--format" "--hba" "--id" "--impersonate" "--index" "--kw" "--like" "--lun" "--mac" "--message" "--module" "--moduleset" "--node" "--object" "--param" "--password" "--port" "--remove" "--resource" "--rid" "--ruleset" "--ruleset-date" "--secret" "--service" "--slave" "--stats-dir" "--status" "--subsets" "--symcli-db-file" "--tag" "--tags" "--target" "--template" "--thread-id" "--time" "--to" "--upto" "--user" "--value" "--waitlock" "-s" )



svcs=$(find /etc/opensvc /opt/opensvc/etc -maxdepth 1 -lname "*/bin/svcmgr" -a ! -name "*.*" -exec basename {} \; 2>/dev/null)

opt_has_arg()
{
    for option in ${opts_with_arg[@]}
    do
        if [ "$option" == "$1" ]
        then
            return 0
        fi
    done
    return 1
}

_comp_handler() 
{
    local a prev action opts
    COMPREPLY=()
    if [ "${COMP_WORDS[0]}" != "nodemgr" ]
    then
        COMP_WORDS[0]="svcmgr"
    fi
    a="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"

    case "${prev}" in
        --color)
            COMPREPLY=( $(compgen -W "yes no" -- ${a}) )
            return 0
            ;;
        --format)
            COMPREPLY=( $(compgen -W "csv json table" -- ${a}) )
            return 0
            ;;
        --service|-s)
            COMPREPLY=( $(compgen -W "${svcs}" -- ${a}) )
            return 0
            ;;
        *)
        ;;
    esac

    action=()
    typeset -i skip=0

    for word in ${COMP_WORDS[@]}
    do
        if [ "${word#-}" != "${word}" ]
        then
            opt_has_arg ${word} && skip=1
        elif [ $skip -eq 1 ]
        then
            skip=0
        else
            action+=(${word})
        fi
    done

    action="${action[@]}"
    action="${action// /_}"
    opts=""

    while [ "$action" != "" -a "$opts" == "" ]
    do
        opts="${!action}"
        action=${action%_*}
    done

    extra_opts="${!action}"
    if [ "$a" != "" -a "$opts" != "" ]
    then
        opts="$opts $extra_opts"
    fi

    COMPREPLY=($(compgen -W "${opts}" -- ${a}))

    return 0
}

complete -F _comp_handler svcmgr

for s in ${svcs} ; do
    complete -F _comp_handler $s
done

complete -F _comp_handler nodemgr

  0707010005c708000041ed0000000000000000000000035ab2183a00000000000000660000000000000000000000000000000900000000root/usr  0707010005c709000041ed0000000000000000000000055ab2183a00000000000000660000000000000000000000000000000f00000000root/usr/share    0707010005c70a000041ed0000000000000000000000035ab2183a00000000000000660000000000000000000000000000001300000000root/usr/share/doc    0707010005c70b000041ed0000000000000000000000035ab2183a00000000000000660000000000000000000000000000001b00000000root/usr/share/doc/opensvc    0707010005c70c000081b40000000000000000000000015ab21754000001fa000000660000000000000000000000000000002300000000root/usr/share/doc/opensvc/AUTHORS    Arnaud Veron <arnaud.veron@opensvc.com>
Bruno Marchetti <bruno.marchetti@amundi.com>
Christophe Varoqui <christophe.varoqui@opensvc.com>
Cyril Galibern <cyril.galibern@free.fr>
FrÃ©dÃ©ric Brin <duckx@mezimail.com>
Gerald Millet <millet.gerald@gmail.com>
Guillaume Benoist <guillaume.benoist@gmail.com>
Jeremie Le Hen <jeremie@le-hen.org>
Lucien Hercaud <hercaud@hercaud.com>
Mathieu Chouquet-Stringer <mathieucs@gmail.com>
Patrick Marques <patrick.marques@cgr.fr>
Stephane Vigan <stephane.vigan@gmail.com>
  0707010005c70d000081b40000000000000000000000015ab2175400001c8a000000660000000000000000000000000000002500000000root/usr/share/doc/opensvc/auth.conf  #
# This configuration file hosts in a single place all infrastructure
# access credentials.
#
# The agent ensure this file is not world-readable.
#
# Each infrastructure element has its own section, with a 'type'
# parameter specifying its driver.
#

#############################################################################
#
# Symmetrix array
#
# This section is necessary for inventoring and nodemgr array
# commands. Each array must have its own section.
#
[<symid>]
type = symmetrix

#
# param: symcli_path
# desc:  optional. if the lookup function fails to automatically
#        find the symcli bin path, this parameter can be used to
#        specify one.
#
symcli_path = /opt/symcli/bin

#
# param: symcli_connect
# desc:  only required for TCP/IP access. the name of the connection as
#        configured in the symcli netcnfg file.
#
symcli_connect = MYCONN

#
# param: username
# desc:  only required for authenticated TCP/IP access. the username used
#        to login.
#
username = me

#
# param: password
# desc:  only required for authenticated TCP/IP access. the password used
#        to login.
#
password = mypass


#############################################################################
#
# Centera array
#
# This section is necessary for inventoring and nodemgr array
# commands. Each array must have its own section.
#
[<array name>]
type = centera

#
# param: java_bin
# desc:  required. the fullpath of the java binary to use to run the centera
#        CLI tool.
#
java_bin = /opt/java/bin/java

#
# param: jcass_dir
# desc:  required. the directory hosting the centera CLI libs.
#
jcass_dir = /opt/centera/LIB

#
# param: server
# desc:  required. the centera admin server to log to.
#
server = centera1

#
# param: username
# desc:  required. the username used to login.
#
username = me

#
# param: password
# desc:  required. the password used to login.
#
password = mypass


#############################################################################
#
# Netapp array
#
# This section is necessary for inventoring and nodemgr array
# commands. Each array must have its own section.
#
[<array name>]
type = netapp

#
# param: server
# desc:  required. the netapp server to log to.
#
server = netapp1

#
# param: username
# desc:  required. the username used to login.
#
username = me

#
# param: key
# desc:  required. the fullpath of the ssh private key, trusted on the netapp
#        array, used to connect to the array.
#
key = /root/.ssh/id_rsa


#############################################################################
#
# VNX array
#
# This section is necessary for inventoring and nodemgr array
# commands. Each array must have its own section.
#
[<array name>]
type = emcvnx

#
# param:   method
# desc:    required. the method used to login.
# default: secfile
# candidates:
#  * secfile: use credentials stored in the navisphere standard location.
#  * credentials: use username/password set in this section.
#
method = credentials

#
# param: spa
# desc:  required. the "A" Service Processor.
#
spa = vnx1a

#
# param: spb
# desc:  required. the "B" Service Processor.
#
spb = vnx1b

#
# param: scope
# desc:  the domain to parse.
# default:  0 (ie local)
#
scope = 0

#
# param: username
# desc:  required with method=credentials. the username used to login.
#
username = me

#
# param: password
# desc:  required with method=credentials. the password used to login.
#
password = mypass


#############################################################################
#
# 3par array
#
# This section is necessary for inventoring and nodemgr array
# commands. Each array must have its own section.
#
[<array name>]
type = hp3par

#
# param:   method
# desc:    required. the method used to connect the manager.
# default: ssh
# candidates:
#  * ssh: use username/key to connect the manager.
#  * cli: use username/password with the CLI tool to connect the manager.
#  * proxy: use a opensvc command filtering proxy.
#
method = ssh

#
# param: manager
# desc:  required. the name of the 3par manager server or opensvc proxy.
#
manager = hp3parmanager

#
# param: username
# desc:  required with method=ssh. the username used to login.
#
username = me

#
# param: key
# desc:  required with method=ssh. the ssh private key used to connect the
#        manager.
#
key = /root/.ssh/id_rsa

#
# param: pwf
# desc:  required with method=cli. the password file used to connect the
#        manager through the CLI tool.
#
pwf = /root/hp3par.pwf

#
# param: cli
# desc:  optional with method=cli. the fullpath of the CLI tool executable.
#        if not specified, the 'cli' executable is searched in the PATH.
#
cli = /opt/hp3par/bin/cli

#############################################################################
#
# hds array
#
# This section is necessary for inventoring and nodemgr array
# commands. Each array must have its own section.
#
[<manager name>]
type = hds

#
# param: array
# desc:  required. a whitespace separated list of array ids.
#
array = HM700.20123 R700.2345

#
# param: url
# desc:  required. the url of the manager API.
#
url = https://hdsmanager/

#
# param: bin
# desc:  optional. the fullpath of the CLI tool executable.
#        if not specified, the 'HiCommandCLI' executable is searched in the
#        PATH.
#
cli = /opt/hds/bin/HiCommandCLI

#
# param: username
# desc:  required. the username used to login.
#
username = me

#
# param: password
# desc:  required. the password used to login.
#
password = mypass

#
# param: jre_path
# desc:  optional. the fullpath of the preferred Java Runtime to use to
#        execute HiCommandCli. if set, this value is exported as the
#        HDVM_CLI_JRE_PATH environment variable.
#
jre_path = /opt/java/bin


#############################################################################
#
# freenas array
#
# This section is necessary for inventoring and nodemgr array
# commands. Each array must have its own section.
#
[<array name>]
type = freenas

#
# param: api
# desc:  required. the freenas api url.
#
api = https://freenas.opensvc.com/api/v1.0

#
# param: username
# desc:  required. the username used to login.
#
username = me

#
# param: password
# desc:  required. the password used to login.
#
password = mypass


#############################################################################
#
# xtremio array
#
# This section is necessary for inventoring and nodemgr array
# commands. Each array must have its own section.
#
[<array name>]
type = xtremio

#
# param: api
# desc:  required. the array api url.
#
api = https://xtremio1.opensvc.com/

#
# param: username
# desc:  required. the username used to login.
#
username = me

#
# param: password
# desc:  required. the password used to login.
#
password = mypass


#############################################################################
#
# brocade SAN switch
#
# This section is necessary for inventoring.
#
[<switch name>]
type = brocade

#
# param: username
# desc:  required. the username used to login.
#
username = me

#
# param: password
# desc:  required if key is not set. the password used to login.
#
password = mypass

#
# param: key
# desc:  required if password is not set. the fullpath of the ssh private
#        key, trusted on the switch, used to login.
#
key = /root/.ssh/id_rsa


  0707010005c70e000081b40000000000000000000000015ab2175400000910000000660000000000000000000000000000002500000000root/usr/share/doc/opensvc/copyright  Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: opensvc
Upstream-Contact: OpenSVC (support@opensvc.com)
Source: https://http://git.opensvc.com/?p=opensvc/.git;a=summary

Files: *
Copyright: 2009-2016 OpenSVC <support@opensvc.com>
License: GPL-2

Files: usr/share/opensvc/lib/rcMd5.py
Copyright: 1991,1992, RSA Data Security, Inc.
License: Other

Files: usr/share/opensvc/lib/wmi.py
Copyright: 2003, Tim Golden <mail@timgolden.me.uk>
License: MIT

License: GPL-2+
 This package is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.
 .
 This package is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 .
 You should have received a copy of the GNU General Public License
 along with this program. If not, see <http://www.gnu.org/licenses/>
 .
 On Debian systems, the complete text of the GNU General
 Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".

License: MIT
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to
 deal in the Software without restriction, including without limitation the
 rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 sell copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.


0707010005c70f000041ed0000000000000000000000025ab2183a00000000000000660000000000000000000000000000002800000000root/usr/share/doc/opensvc/provisioning   0707010005c710000081fd0000000000000000000000015ab2175400000a2a000000660000000000000000000000000000004200000000root/usr/share/doc/opensvc/provisioning/provisioning.agent.debian #!/usr/bin/python

import os
import sys
from subprocess import Popen

class provAgent(object):
    dev = "/dev/fd0"
    data = {}

    def __init__(self):
        self.get_cfdisk()

    def get_cfdisk(self):
        if not os.path.exists(self.dev):
            print >>sys.stderr, 'no cfdisk'
            sys.exit(1)

        with open(self.dev) as f:
            buff = f.read(1024)

        if not buff.startswith('todo'):
            print >>sys.stderr, 'nothing to do'
            sys.exit(1)

        for line in buff.split('\n'):
            if len(line) == 0:
                continue
            l = line.split(';')
            if len(l) < 2:
                print >>sys.stderr, 'malformed line:', line
                continue
            self.data[l[0]] = l[1:]

    def set_done(self):
        with open(self.dev) as f:
            buff = f.read(1024)
        buff = "done"+buff[4:]
        with open(self.dev, "w") as f:
            f.write(buff)
        
    def set_vm(self):
        if 'vm' not in self.data:
            print >>sys.stderr, 'no vm in cfdisk'
            return
        h = self.data['vm'][0]
        with open('/etc/hostname', 'w') as f:
            f.write(h+'\n')

    def set_ns(self):
        if 'ns' not in self.data:
            print >>sys.stderr, 'no ns in cfdisk'
            return
        h = self.data['ns'][0]
        with open('/etc/resolv.conf', 'w') as f:
            f.write('nameserver %s\n'%h)

    def set_pub(self):
        if 'hv_root_pubkey' not in self.data:
            print >>sys.stderr, 'no hv_root_pubkey in cfdisk'
            return
        try:
            os.makedirs('/root/.ssh')
        except:
            pass
        h = self.data['hv_root_pubkey'][0]
        with open('/root/.ssh/authorized_keys', 'a') as f:
            f.write('%s\n'%h)

    def set_ip(self, l):
        if len(l) != 3:
            return
        dev, addr, mask = l
        buff = """
auto %(dev)s
iface %(dev)s inet static
    address %(addr)s
    netmask %(mask)s
    gateway %(gw)s
"""%dict(dev=dev, addr=addr, mask=mask, gw=self.data['gw'][0])

        with open('/etc/network/interfaces', 'w')as f:
            f.write(buff)

    def set_ips(self):
        for i, l in self.data.items():
            if not i.startswith('ip#'):
                continue
            self.set_ip(l)

    def reboot(self):
        cmd = ['reboot']
        p = Popen(cmd)
        p.communicate()

    def setup(self):
        self.set_vm()
        self.set_ns()
        self.set_pub()
        self.set_ips()
        self.set_done()
        self.reboot()

provAgent().setup()
  0707010005c711000081b40000000000000000000000015ab217540000016a000000660000000000000000000000000000003d00000000root/usr/share/doc/opensvc/provisioning/provisioning.example  /opt/opensvc/bin/svcmgr -s test create --resource '{"mode": "lxc", "template": "http://download.openvz.org/template/precreated/debian-6.0-x86_64.tar.gz", "rootfs": "/opt/lxc/rootfs.test"}' --resource '{"rtype": "loop", "file": "/tmp/fooloop", "size": 11}' --resource '{"rtype": "fs", "type": "ext4", "dev": "/tmp/fooloop", "mnt": "/tmp/fooloopmnt"}' --provision
  0707010005c712000081b40000000000000000000000015ab21754000008f2000000660000000000000000000000000000002400000000root/usr/share/doc/opensvc/schedule   Schedule Syntax
===============

     [!] <timeranges> [<days> [<weeks> [<months>]]]

     !
        desc: exclusion pattern. ommiting the ! implies an inclusion
Â              pattern

     <timeranges> := <timerange>[,<timerange>]
       <timerange> := <begin>:<end>@<interval>
          <begin> <end> := <hour>:<minute>
          <interval>
            type: duration expression (default unit: minutes)

     <days> := <day>[-<day>][,<day>[-<day>]]
        <day> := <day_of_week>[:<day_of_month>]
          <day_of_week>
             * iso week day format
               type: integer between 0 and 6
             * literal format
               type: string in ("mon", "tue", "wed", "thu", "fri", "sat",
                     "sun", "monday", "tuesday", "wednesday", "thursday",
                     "friday", "saturday", "sunday")
          <day_of_month> := <literal> | +<nth> | -<nth> | <nth>
             <nth>
               type: integer
             <literal>
               type: string in ("first", "1st", "second", "2nd", "third",
                     "3rd", "fourth", "4th", "fifth", "5th", "last")

     <weeks> := <week>[-<week>][,<week>[-<week>]]
        <week>
          type: integer between 1 and 53

     <months> := <monthrange>[,<monthrange>]
        <monthrange> := <month>[-<month>] | <month_filter>
          <month>
            * numeric month format
              type: integer between 1 and 12
            * literal format
              type: string in ("jan", "feb", "mar", "apr", "may", "jun",
                    "jul", "aug", "sep", "oct", "nov", "dec", "january",
                    "february", "march", "april", "may", "june", "july",
                    "august", "september", "october", "november",
                    "december")
          <month_filter> := %<modulo>[+<shift>]
            <modulo>
              type: integer
            <shift>
              type: integer

Examples
--------

* schedule = 16:00-17:00@1m sat:last,tue-mon:last * %2+1,feb-apr

  reads as "once a minute between 16:00 and 17:00 on last monday,
  tuesday and saturday of every even months plus february and
  april".

* schedule = ["06:00-07:00@1h *:1,-1", "! * * * feb"]

  reads as "once between 6 and 7am every first and last day of every
  month except february".

  0707010005c713000081fd0000000000000000000000015ab21754000004d0000000660000000000000000000000000000003300000000root/usr/share/doc/opensvc/template.comp_module.py    #!/usr/bin/env opensvc

import os
import sys

sys.path.append(os.path.join(os.path.dirname(__file__), 'com.opensvc'))

from comp import *

import files
import packages

syntax = """syntax: %s check|fixable|fix"""%sys.argv[0]

if len(sys.argv) != 2:
    print >>sys.stderr, "wrong number of arguments"
    print >>sys.stderr, syntax
    sys.exit(RET_ERR)

objs = []

try:
    o = packages.CompPackages(prefix='OSVC_COMP_BDC_DHCPD_PACKAGE')
    objs.append(o)
except NotApplicable:
    pass

try:
    o = files.CompFiles(prefix='OSVC_COMP_BDC_DHCPD_FILE')
    objs.append(o)
except NotApplicable:
    pass

def check():
    r = 0
    for o in objs:
        r |= o.check()
    return r

def fixable():
    return RET_NA

def fix():
    r = 0
    for o in objs:
        r |= o.fix()
    return r

try:
    if sys.argv[1] == 'check':
        RET = check()
    elif sys.argv[1] == 'fix':
        RET = fix()
    elif sys.argv[1] == 'fixable':
        RET = fixable()
    else:
        print >>sys.stderr, "unsupported argument '%s'"%sys.argv[1]
        print >>sys.stderr, syntax
        RET = RET_ERR
except NotApplicable:
    sys.exit(RET_NA)
except:
    import traceback
    traceback.print_exc()
    sys.exit(RET_ERR)

sys.exit(RET)

0707010005c714000081fd0000000000000000000000015ab21754000001a3000000660000000000000000000000000000003300000000root/usr/share/doc/opensvc/template.comp_module.sh    #!/bin/bash

PATH_LIB=$OSVC_PATH_COMP/com.opensvc
PREFIX=OSVC_COMP_FOO

typeset -i r=0

case $1 in
check)
	$PATH_LIB/files.py ${PREFIX}_FILES check
	[ $? -eq 1 ] && r=1
	$PATH_LIB/packages.py ${PREFIX}_PKG check
	[ $? -eq 1 ] && r=1
	exit $r
	;;
fix)
	$PATH_LIB/files.py ${PREFIX}_FILES fix
	[ $? -eq 1 ] && exit 1
	$PATH_LIB/packages.py ${PREFIX}_PKG fix
	[ $? -eq 1 ] && exit 1
	exit 0
	;;
fixable)
	exit 2
	;;
esac

 0707010005c715000081b40000000000000000000000015ab21754000002bb000000660000000000000000000000000000003c00000000root/usr/share/doc/opensvc/template.node.arbitrator.conf.gz   ‹      ÝTMo1½çWŒš­”„‡•Š.”âàîÎn¬xíÅc'äßóÆIo·œØKâ]Ïûš±çós>³9ñQ8lŠ&…x¸3>€;ov³'³óW?gªwÍ»mˆíÍ‰Ö›ñáúŒæ"MÍƒã#ÓgãD‰(ò¯l#Ÿ4ÜÇ\>´Ü™ìÒIÛ×àù‹	²9î?ÊÈílC·Ô³çh›bÒ`ÇýŠë>G–à6*†|h¹8^Ýùz“2/›¦êk)¸ŒÙ{ë{J Î’£€mÔ‚¿ÂkT7.KâX*°2@—5u!Vh†6!1mWìÖcÊet6©ª£$ŠL)ZnÉú
E û†£³Q<šfU,Vžz»a9.é‹‡*AŽ™
•nÏ¾ ã\U¿À¿v‚†èöý‰ª‹aPš#Üìáæ.‘cƒ7h¤òbï fŸ`N+4õ'9nìFÓ?ÂNBR;Ç ü *`'ª£täÁ‘¹_)·òR K\àH~N³¢@3_¤
î™d­Ô:RhµÕžøBr‚¯g¢"B4ÏÓ¥Cíß–ô1§É¸¦©I¸Bõn?èng¬ƒóžsIßu˜Ýnqalšäf=¸ÆÈš‘œ›0zÊT–Ñ4kli[sÖ³,+ŒºWyOGm@ „SènÃ² Þ”C±oM‘²k1“2«æh¢Áö«¤ÍXû°…òÚï’Ýñ`•éhm×qDX“¡—1x±ÈYžÌÓ>J,ê:‹NÞ…ê¡4êêúZ—ÚûêÌéår{ fïô/}Àfÿ¸{÷FþÃÛ÷Ð!±ŒYÔ“æ›¸ÓË–Ë/®Îdp`Ô}˜·6­žÜ$U¦—wŸ¾½~óöª¤z€>äúh[co   0707010005c717000081b40000000000000000000000015ab21754000000e2000000660000000000000000000000000000003700000000root/usr/share/doc/opensvc/template.node.asset.conf.gz    ‹      ­Q=oÂ0Ýó+NÊÀDâ‰DÇþŒƒk¿«iìÞÙ¢ü{œ´„V¬~ƒ‡»Óûr]—DUSAÌtZ±]Adº²ÝUoKØS5ýÀõâÙîzbØ4"/·1—"Æý>bU{Õ£ÌBÄøJŽaŸ7½Nc|Tj¯ÔVíò{çåàŸ	0®w†^èŒ	ìÌ4‰ÉÝo<
šõ'"˜zÏÐ&$–n64yÒ&:?5Ôþ4	·ùŽÑZoÚ{]+Ë:ëõw“ÕëìøÏ~u†K?>    0707010005c7b2000081b40000000000000000000000015ab21754000000dd000000660000000000000000000000000000003900000000root/usr/share/doc/opensvc/template.node.brocade.conf.gz  ‹      ­R=OÃ0Ýý+NÊÐ‰f§‚‘‘¥#b¸Ú/Ä"øÂ-ÚiŠÄê7ÞÞ—Ýu-á:jˆ…î¤â9 ]CTº¶Ý¹—5ì«[¬¾ãò%îoŠæG„2¡.ïb©Å¼Ì|š°©=ñd‹)>KT„ÿ›€Ë”oŸ%áJªÞ¡¿K›áã==Ò4úŸ„•Á|½8®¹hfåd(¢”GÐn.6®µì(Iý	ìs”´§#ðçA‹io#+ú ¾¿6µñl»¤Ìç}Õ?l³‡z&Î}Îò$œ     0707010005c7b3000081b40000000000000000000000015ab21754000000db000000660000000000000000000000000000003900000000root/usr/share/doc/opensvc/template.node.centera.conf.gz  ‹      ­Q=oÂ0Ýý+NÊÀan###b¸ž_‹`‡³#àß×iC¨Äê7ÞÞ×UUI˜Š
b¤øåRt‘éÊvgŽSØj}2£Ù3· öã¥¥…:äå² Æb¢„ž¿:Ìj;îâ(DŠëàö}cÑðÐ¥—Á}ðx’²wèß2ö×8¡-}ÃCü&ÌQòÅaÊE=+_K &(¥´˜JYÄ’\ð+: ÿÞ9D­cËŠÚ©Ÿ-Íó >|âû*kÎ³M>Æü ÃÆ¬*	   0707010005c7b5000081b40000000000000000000000015ab21754000000e3000000660000000000000000000000000000003800000000root/usr/share/doc/opensvc/template.node.checks.conf.gz   ‹      ­Q=oÂ0Ýó+NÊÀñÄ jÇþÆªƒ9¿‹4w¶Zþ}UW¿ÁÃÝé}¹®K¢ª© &:îÀg-FW™®lwÕûö£šœžqý
âvAÍk—zäåº ¦V”Ãh=µ7Ûë$D‚Kò÷ãÐÚÔÇ‡AcvÆ¬Í6¿wNPŽ ™otûÖ3½Ò	ÄóoÐL¤œ/·x4Z±ŸˆjƒPì@«1i7—³¢!8åèÃ°¡ðô«I¥ÑÎ
¸¹÷µÐ,½Ñ~o²ü~™½üñ_ý Ùi¦Ÿ   0707010005c7b6000081b40000000000000000000000015ab217540000029d000000660000000000000000000000000000003900000000root/usr/share/doc/opensvc/template.node.cluster.conf.gz  ‹      í–Mk1†ïþ¾&.ÚCk
m¡—^’žB²4k«Õj6úp0¥ÿ½3ò®½›â’”%˜R]}ìèÕûHM§c–ÉF,N»†±ÂX8Ü¸ÞMnÛÅÞMDêwÜ=P0o3¹ýrÄ"ŽDMZ9<Lt2JGÀûl|R.–ƒ•Ê.¥ÝÞu‘X3†}WlPÛÊjXÂ=«e˜&¿ÅÐûÛÙ˜Ê’9pÔÜ~³ÁÒT'n„D1ŠbX‰‡p ¤,Ç¬5’ã¨qŸ+zÑƒ-c#ª 7ð`^Á
KD<[íUËÙ¼Pj ¼éôÍXî;Q²¥ŸÀeÍ™Òš«œè²Q	ÍòÉìˆ¬p¨(Ô*YòÀÕ
“Þ *P]LÿFLJS]³•Ð¨#eX¨F»Å¾·ƒ8€0/[Ãÿ&÷„ßBæLoÎ×hñŽ­îtžp8¢˜ÎÔãyàÅóòd·6°a•|•äˆ¹Ññ„âF±–Ö†|\á½»&½2X¾`TR|Ó¤¼ÿx}õúÍ¬G àÜ’È£8ð#ÍaHXø^m„Å“V}ê¼ÈÍú0ÌžAóy9§°Ý%©<û †õ8öPÙ#ÃÖê’”*»ÎaXtYC¼þ)S·™J6Î EqW‹¶ÊåöX–ä·€ŠèEšÿ _dg÷€e$bÙýùgœ÷™B®ÿU˜+"‡Êx^o(;Ã—\lœMlÿºF_ž~©wõ
RéÍV[!õa/)ôÌ/Â»`AÁiäž3îŸ{*T\
+›˜.ñó‘ßƒ;Ê EÚ!Z ç¬_CnÖA,rxÙžŸ‹|ÅcM>°{dÖ_.YâQ     0707010005c7b7000081b40000000000000000000000015ab21754000000e8000000660000000000000000000000000000003500000000root/usr/share/doc/opensvc/template.node.cni.conf.gz  ‹      Í1‚0…w~Å%Ì‚³F'7ã í›¶¶%†ï! ãÖÁ›š»ë{÷¾4YI
«—šbÊE,–‹Ë.9sØtyIúC¯ØÝ“«ÙÏª¶&íy¶ˆX=/Œ-J…/³}¡<ö‡·–Êï‰ÄªhU˜ïË9ÈKÒ“(G@7,y‹‚*°…5:Ï ¬äoœÉ^"×Ac| ]Càîîx˜Âgüe=¾aóéù›0º¢úï©i™ŒÏe™Çu¤Ðº"ÑP‘ÂåÐG9’< ´ÿ@  0707010005c7e3000081b40000000000000000000000015ab21754000001d7000000660000000000000000000000000000003c00000000root/usr/share/doc/opensvc/template.node.compliance.conf.gz   ‹      ÍR±ŽÛ0Ýó2x¹K‚:¤h‡:ßm‡¢P$Ú"‹*%5ñß—²/ŽEÊÁ0Hê‘|ïm·kÆf+FÓ4g•×¸
ÜŠ!për·y½û}S¶=ãx!6ÇûÐ¨{4Ù¡WŒÂLÔÔÉá2í«r±ÆŸÙ2š?+[•]º/xx:‡ò½a"È	ÈsO¨mk5|†=²ÕÓ¡µt<¿A±0!CK©Gh*'¨œ¨OF~u²äàÒ[ÝW>ƒr*ˆ”7ÐÚ+wã’lê:4ÐH©6;xF¬prä}ìãÞÞßè_¶ZqôI]wrÍÇ%÷éQ´þ‘ƒQé¿u©ü“œÂ=ù_ÈÀ‰È¡òï¤þÖBÄ‰à…3>LòØv"¯¨s‘µ %¼&°ÞX-‰Šå¦R‰³X`~&N™Ù,~i ©½3Cd~ém”é]'BõèB¬àÎˆaÚ¤zwsŒõEk›ú©‡±EÆÒÂ(ÚD<î*°/xû Í·EÙ‘mÁA¼;§eá"Yßa¤CŽ	N2^¶ÙU`Rå·Uªq  giFÇÉ…•¯Äˆ³„›ß“ GŒî   0707010005c7e4000081b40000000000000000000000015ab21754000000d9000000660000000000000000000000000000003500000000root/usr/share/doc/opensvc/template.node.dcs.conf.gz  ‹      ­Q=O1Ýó+,ÝÐ‰ÞNEGF–Žˆ!uÞõ"Žä°Ñþûú€^‘XóFÛz_îº–p5ÄBX[Ò5„ÑµíÎ½ZØ7·Ø|Çå+Kx¼«)u‚-b©D9Ïþ8aU{ö“.B$ø¬Qþo_§r7ø’nd óùYêŽCdÚÓ		ù;¡1(ÛÅá7Í^ü
„†,TFÐf®:Z%J9€<—˜Ó–ÀŸGV•^G/èCæþÖÒÊ±ô’Š?oM{·Îžì,;wÈ3     0707010005c7e5000081b40000000000000000000000015ab21754000000e3000000660000000000000000000000000000004100000000root/usr/share/doc/opensvc/template.node.dequeue_actions.conf.gz  ‹      ­Q±nÂ0ÜýOÊÀé\#cF„*×¾‹ÔïÙjù{œ6	ˆŽõm~÷tïî\U%¡**ˆAÎâ’ð®MtÁËå
"Ë•íNžÂV/G5˜>ãúØ¾Þo‹iaS‡L.b(HLèõG‡ùÚNw2"ÎîÃþe,ºx7ø<&1Pöþ%¥‡q3´¥<Ø™Ÿ„YALÞØ¹¨×¬?ÁÔ¦Ø‚c94–³ ìôZÑxøÞ$\K«µ¦žÚšµæ\}Ôß«ìa=Ï6y-(uìƒ]"   0707010005c7e6000081b40000000000000000000000015ab21754000000e1000000660000000000000000000000000000003700000000root/usr/share/doc/opensvc/template.node.disks.conf.gz    ‹      ­Q»ŽÂ0ìó+¥ ‚¸¢ åý %¢0ö„Xäâ°kø{œ ­§p±»š—Ë2'Š’2b ³NN’.#]ÞîŠÝv_FO¸]<ÛÕ[OL[¤å<#†RÄø^ZLj¿º•Aˆçèö{cQëØ†·A¥VJÍÕ2½/NPŠ ~ÜHãjghCGt`gÆ ‰HLºØ>ãQ¯Yÿ!€©öL¡Íú(ÍØÍŒ:oAÚç»m?Â•4šQYoªW]Ë4[ôu‘Ô×ÓìçŸýâÿ  •     0707010005c7e7000081b40000000000000000000000015ab21754000000d9000000660000000000000000000000000000003500000000root/usr/share/doc/opensvc/template.node.eva.conf.gz  ‹      ­Q=SÃ0Ýý+t—¡Í^FF–Ž\a¿ÁN%è¿G¡mÊ«ß(éÞ—º®%\G±Ðá“[Ò5„ÑµíÎ½XØƒ[l¾ãô•%ìnjêG„:Á–w±T¢>Ïü:aU{âI!k„ÿ›€ëTnŸsÂ•dÞ!ç¥Îðqˆžé	ýoBcPoûK.šYøBC*#h3W­’¥@ìKÌiK{àÏ#«J¯#ú}miåXzJ…¿·¦}¿Îì,;÷©1Ó´     0707010005c7e8000081b40000000000000000000000015ab21754000000de000000660000000000000000000000000000003900000000root/usr/share/doc/opensvc/template.node.freenas.conf.gz  ‹      ­Q=oÂ0Ýý+NÊÀTÒÔŽŒ,ŒU‡Ã~i,‚ÞÙþ=NB¥®~ãÝé}]ÓÔ„i¨"&ºN€ÀZ‹®"
]ÝîÌÇ¶yý4“Ùn—(nóÔTÛÃåeùRS1jãÈÇ‹ÚŽ„Hð½Àýß8tœ‡ô4¸2Pñù]êë;oé¾ Þþ$,jËÅaÎE#Ÿ‘ ÔE¡ÔƒVcÖ~.fE!:ÛäcXÓøóÒ¬ÒjÏ‚ÖEÛ>šZx–ÞBâëºèo—Ù[9‹ÆÜ'jÙú    0707010005c7e9000081b40000000000000000000000015ab21754000000df000000660000000000000000000000000000003a00000000root/usr/share/doc/opensvc/template.node.gcedisks.conf.gz ‹      ­Q=oÂ0Ýý+NÊÀéÜ
FFFÄàž_ˆE°Ã­–_§mB¥®~ãÝé}]ÓÔ„i¨"&ºÃy½j%ºŠ(tu»3§9lór6“Û+QÜëST¹‡ËÊr]S3Êq´ïµ½t"Á={û¿qèlÒÓà!Ìd âò³Ôì;Ï´£ÄówÂÂ \.Ž¿¹h´boHê¢PêA«1k?7³¢Èrò1lèüyjViµ·‚ÖEnçª¢e ìç¦x[fÛrùNñ“ë   0707010005c7ea000081b40000000000000000000000015ab217540000015d000000660000000000000000000000000000003900000000root/usr/share/doc/opensvc/template.node.hb.disk.conf.gz  ‹      ½’½OÃ0Å÷ü'e*:°PÁˆÄ S7ÄàØ/Í©ŽüÑªüõœSú!U,qCÙ¾çwï—ºž²ªš&¬"×5×”vÈp\ÿ]nÂ¹i³«Þº¦¾}¯ã¼ãÄUU\¯±Ûú`îO—ldýfÂ*áDíÕX/zR6¢ì|d8YX†<n´*Ût²öêZ q°ßŒ4·¬é‘Vp¬ÇÙD!j9±ìP¦bJž¶“|ÈZ¨Ë²rF¬(CmðýŒžõ9&jp†ÖÀ°V	¦4£Ð{G9¢tDŠü	ŠÏÖH'Í_èª<ÒVäÎÔœ7˜‰Ñ…x&­÷?!IÜÃçôXá_P9òºÀ2¿û=«všÓ{ VIâ¼‘ŒžTøŒHHI¬­çP´w‘åVv+á¸?&ï•ü.c¾ßñIÆbµú„ ÿã—     0707010005c7eb000081b40000000000000000000000015ab2175400000170000000660000000000000000000000000000003e00000000root/usr/share/doc/opensvc/template.node.hb.multicast.conf.gz ‹      Í”ÁNÃ0DïùŠ•r…ˆ*¥=ò½!Ž=i,R;Ø¨ÏnZH8TH%‡îÅJ¼™yq’çSV–Ó„%ruyEiß‚v]“¬V1ýKnÂb¹iÙe/u™ß¼fË>ïjHœebýûOÌãà@xãzÂDQûV•~&mBÙxïlÀ`áY5±ß1¨»¼ÍçwÅm1(îgß’ vpè‰-´­¬¦5má¬î3²PÔÜ±©Ç/\r"FJž"œ‘UñÒØ˜àÈ»‚Ÿ]JC>ÍºT](6ÉíTê‚jÄ&B¥4¨òž$ßú\–ƒ³+­3=2ÁÈþz‚cëCºPŽ³®saI®>>”®ÉyƒñÉãû¿^ß¾:Ž<*Ù|w±´ç¢2hÔž¢ur²øº‘Ï5@Ã~ÀPR	Å4ƒß‘:°,Ál1úkï¢å©ÖmYÄÆC¯[ïÐ#>âÊ‹,û™í³  0707010005c7ec000081b40000000000000000000000015ab2175400000165000000660000000000000000000000000000003a00000000root/usr/share/doc/opensvc/template.node.hb.relay.conf.gz ‹      Ý“=OÃ0†÷üŠ“²€ÄW?ÒªH0²Ð18Î›Ä"µƒí´äßc;MS©ê'+7¼§÷î;q<fD1Þ®L¯È¶5H£bí¯íFg7.»è­Lã»÷hö]uG‘ûíNéì~èÞ‰1]žáªfi…C«gVxEã³ÃkÝ!CÎšÊÃ½(‰Þäæ†îDSƒ‹\pz Zð°s0ÜU¬Ëþš5Œª¶~’*s¶Á«]vòž—&ç pû	u‹‘UÔøÉu[ÛÛ!SÆ,#|ñ’Éí„-Éöhþ‹Ç§×i²¸`÷®+šLgý™Ï¦I2Ÿ%‹äd+6PÍŸPîYž@>à?¡<I~Ê8ÏÌÉÐUÌX×™ClÑLÛÌR®Õ†X÷FSäJãˆ/WÒ×UÈÂ™Ó•¹\¸ÛÐ÷ø<u‡øqúY'‹     0707010005c7ed000081b40000000000000000000000015ab217540000017f000000660000000000000000000000000000003c00000000root/usr/share/doc/opensvc/template.node.hb.unicast.conf.gz   ‹      Í”ÁnÂ0†ïy
K½nˆ¸Œ±ãž€Û´Cš¸ÔZqº$ÝÄÛÏN¡ì‚&A¸‡¨²õÛþâ¸ªæ4SÁŒ¦rmý ùÐ#LÎ¦|£ÜŒ&ró²3ïm]-?Ìºô»9ulŒþ‰‡Ÿýó9¿õ>ŠãqFS@É…ÞÖN™¶q@uDü(â¹„7Û¥âñØØ¡ËçÚ–‹òA"t”22ñ,{Èr‹¢•B÷8xd»Çš½.Ne HÇGÝÔ££†¼Â#¹ÂE’''[Q¥¾@Á” 4€ÖµE_ôÌZÂôÖÊ.Üqnîô.Û<DÛi™ëÆ¦^”ÉëÕ°'-!Wû‚YAæ“^àØ‡˜ï”ãÓRìZXÚWiJ¥¦S¥ç4‹¸P,á›cÊ¨2í1wKku-*=@"ÖÉ’ÿN—~D‡¤¯²EsVhÆ°;²¬QØâŸõî'’¬ú¸sKi“sxÜG|JyeÌ/)®‰   0707010005c7ee000081b40000000000000000000000015ab21754000000d9000000660000000000000000000000000000003500000000root/usr/share/doc/opensvc/template.node.hds.conf.gz  ‹      ­Q=o1Ýó+,ÝÀTn§¢cGFÔ!$ïšˆ#¹Ú‰€_-G¥®y£m½/w]K˜Žb¦^ZÒ5„ÒµíÎ4ì‡™mžp»dö›§š¸ _Gèò¥!æJÄåÉG,jïv”Yˆ_52üÿÇ`ëXžw9áARïàŸ¥LpqˆŽÞè	Ý=¡2ˆÓ‹ýo.š,Û3
˜†ÌTh5U	ZÉŠRö ëJÌiM{àÏ#«p/Á2zŸ]ÿhiáXrKÅ^×ªýºÌ¶z–ù–m˜     0707010005c7ef000081b40000000000000000000000015ab21754000000df000000660000000000000000000000000000003800000000root/usr/share/doc/opensvc/template.node.hp3par.conf.gz   ‹      ­Q=OÃ0Ýý+NÊÐ‰¦¨ŒŒ]2VÆ~©-RÛ½³ý÷8„¦H¬~ãÝé}]×µ„ê¨!f:—“æftQéÚv§ŽKØn÷¦f¯¸~F¶OwI1¶L¨Ë‡†˜{“~Ÿ°ª½êIf!b\ŠgØÿ‹Q—)ßbÀT½ƒ—¥$?zC/tB {ó“°2ˆ©Ão.ªéõLcdÊ´IEÜÒË†B´ m²aKðç¡E¸§½¦¿µÒ¬¹†¬¿¶UþyíëYTê÷Ý¨E   0707010005c7f0000081b40000000000000000000000015ab21754000000dd000000660000000000000000000000000000003700000000root/usr/share/doc/opensvc/template.node.ibmds.conf.gz    ‹      ­Q=OÃ0Ýý+NÊ©3¨ŒŒ,ƒc¿4©mîlAþ=NhR$V¿ñîô¾®ijB5Të¯VêÑUD¡«Ûz[Ã6ïj±úù+°}¼+Šaó„²<TÄR‹˜u?aW{Ñ“,BÄøÌŽaÿo,§t7ø<62Pñþ]J„qƒ3ôLx°3kÂÂ ¦\œo¹(jÖW$0) 6f×ZZòÁ‚´I.ø#?ïÌÂŒšÑÙ`º­§eÈì“þ>õ§}v*gA©vkœ¢	     0707010005c7f1000081b40000000000000000000000015ab21754000000dc000000660000000000000000000000000000003800000000root/usr/share/doc/opensvc/template.node.ibmsvc.conf.gz   ‹      ­Q=oÂ0Ýý+NÊÀT²·jGFFÄ`Î/E°ÃÝ–_§)‰Õo¼;½¯kšš0UÄDçgýâjtQèêvgösØƒ™œžpýŽâ^ï‚Ê=\P–/1µ¢G{°¨mì “	.ÙÜóÆ¡³yHwƒÛp#ïy©#Øwžéƒ> žÿår±ûÏE£{F‚P…RZYû¹•…è@–“aM;àáY¥ÕÞ
Z¹½µÐ,½†dÖEþm™½—³hÌ/«ÜÉ	  0707010005c7f2000081b40000000000000000000000015ab2175400000166000000660000000000000000000000000000003a00000000root/usr/share/doc/opensvc/template.node.listener.conf.gz ‹      ÍS;OÃ0Þó+,uèEL 2"±³!c¥VÛõÙ…þ{ÎNó*$„2p‘¢8wù^v‹9«Yˆ«ÀYC	q&¸‹áæÍ®yíÍ¾5Eë§OõýH)µŽÜ¸ž±J&¤|ïÓ“´„Ò‰8d¡/;[™mÅÝ¬êÕãA°tÄ®OÊlâ£ø3ªdR<ñ²ƒ0¡º‰ŸµDëÝ¸õm¦t^	ïVüíCÝ¤?ä|Lÿ5¯õíúî¯a_¿IJ<;²µBªdx®õW“¿ @(o-TòQœ&!ERA©ö`_gñÈè¼r>™í©¼Œ2bÖqP™Êî‘±ªbÙž££êµâ*'P^jÔÉK˜%SiôÂsà[„‚9žQ{Ë«šEQ¶H¬ÑÐ‹FŸ,dÚI"¤nÒ¥ªÍPz†°¦î«ºz+ÿ¦ÛÎæ3qv÷N    0707010005c7f3000081b40000000000000000000000015ab21754000000dc000000660000000000000000000000000000003800000000root/usr/share/doc/opensvc/template.node.necism.conf.gz   ‹      ­Q»nÃ0Üõ<xJ¼§hÆŽ]<éµ%—”äï+×± «n$‰{±ªJBUT3‡q2£+ˆLW¶;õµ„ýV³ÓÜ.íá!(¦‡MòrWs+bÂ¤O6µ=È,DŒßäöucÑé4Ä‡ÁÏà±’²wð²”)çêœ¡#áÁÎü'ÌbòE{ÏE“f="‚©L±ÕS’~i¥&,H›è‚ßS<½3	7ÒkFcƒiÖ¢6šm 7õuŸåß¶Ù{>JýIPÀC	  0707010005c7f4000081b40000000000000000000000015ab217540000089d000000660000000000000000000000000000003600000000root/usr/share/doc/opensvc/template.node.node.conf.gz ‹      í[moÛ8þž_A 8ä®»Éî¶‹ôÚà‚¶»è×iz_‡€’Æ67Éå‹Ý÷ão†”,Ê±ÜlbînüA$rH>Ï3CrÄ>z´ÍßÞ#¶Å™Sº‚-šÛâÍm»½_h°ÿÝ£~^ÂÕBÛêy×\-ÔŸs!/j¨±Äh‹?Æ•ÚðBÂ²ÉŸ¸t@o,ü„…êæ›
&<Hßõò»¿´¦€a÷Á¦WÎ@)&¢d'l

¬(©X©ÕlVÛ‰kˆ£GÃ®Äçç3 q‹:ÔË^°õ”!Ú^1¯—R/°ÅrÎ[î…Vc´ôhì%õï‹ðº7»ŠïÑáW˜ ¸¼±äËØ¿€CÕ®ÂúN+¸5®+ðñàgˆÑ%(f¬ž‹
,P”øªÔRBéµeZ±}rîzj±_Sá<Øý!Á‚ÀM´Ô|Wq;ÿxþ{äÈU%*îÁ5^¿ùû{}ö¯?½?ÃëÛwçxýpv¯¯ãõ_tM7go^áõãùÏx=ÿ7UÃàõÓéy—Sœ6Ôˆê8°sQµÏ<+€V‡éÌ³`ˆÛ!nX|ºÏ&ŠÜ Vvž[ßÖl‰sÔ_=¢Á8áCô‘¿E†‘?$˜  ˜;þb‡i¾«{œª46&ÔDÛ:bBáÄG.sÓGèE¬q1 1†YQ	5M‚µ ¼¼B.4…«9—"ÒK ïVW¡¤#>ÿ|a¸ÅØrW‘?:¼O¸ÊÃlŸ“Ê9>F}=a/r$N˜²LG,Ù PÒ5ö‹+ÐÁÉ«œln^R\ZìÈŸìcŸêÝÞ¥é"k'NCÔ‹Ïv•“ñu„CYur×yÃ•N|ÏXG³òØŠ5ÉIpø,¸n&14m8,Ò¡Œ!†« Têò¢ÔAy»³°~+A&ƒ’æm»ráðÞÞˆ#Åtn…Û„÷µØÙ•û·7ÂˆhÿøôðèÇMhóª²pßnÂ‘ðÆ™W§’+ÜfmÂ½B’íìï}‹%âÿÊ^áê^nB~"µ~ü`@R>æhÜVëÍt}KhŽq½ýÝF´yyù€öÐFí³Ñv€slÑÚ÷@»ÅÑ®êëÁPâ×q/ü øý ï¤ŒÖÛwƒ)­XÐc´õ oôË˜HüôaöR+…Í^x½« ¿WìçWo:\j©ÜA[ÄµÐµ¯nBheŽ2»OS‡=’tÇaÐZ8tåæå®²zWWúˆôÁ
JDö)©¹Pìs-­)cì8&Î÷3¼ 3èIÆTk§IÔK˜xTÓ¨Æìí$~ ‚ca>¥¬[¥ÁÅç)5¶´¦cê2(Qrl!°MÞu.2¾¤	ŸyoÜó'OºÍ»1šæ_)H»JíG^SúQ{C;hˆ`\U,Æ#î:u¦(	*Å*1™ 9K$ìN.¼InÛI”vŒ «[ü€ 
K›ý]¥þ¾^í¹‚Ç ÙºIò¸4èDU¥JjNßLƒ™ZüÃ!†Rf´Í´Â`àg¼«¹êÉŒ6—S&j£ãG‹eã™Tù€-f¢œ1‘œÞy9(ê@ãã&‡ÖØŒªHUi5>¢’ÊÿQ‰Ìü¯O¥Á­þï„§Ãä½¨Öþj]‰äL¯…17Tp)³º$	ä…¾Õ¨K$eA2[ËoéÁÐ÷däi?6¼òœú‘}‚bìÍg0*23•uÝyJ,<Ïx={ÿþ|yó÷Ñú±õžŽÇ8ºñôš–8jJ¬IË6:Y_:/Û`ïò62k½Ö+§ï=!Éï]PÚði¿OÖÔY+½D‡xÒÞöÊ§­ÑÐËŒžŽ±Ö¯œøá˜
ômm2€¯}qM^KÌ633NÌítLOqN¶f“#'þì¾¼ê	}Ï\ëÛe^—™ÎLÝÊër5ßsO÷¹ªsQçû’î‰3³´VÛIÚìËµ7Š|­Æ{ïog©/våRïQç·XƒR9Š5"½gK¼Iiíüð…éiíä“Úè·ò‚¾±³ÍÐd3`ë“N+/zÙé«+z‹Š0ýá>›´³PkÜ!Òi×ˆÿÒ?ROEL7ð¶@<$×˜ÎÑ¡·qWªL+âÜ\œg3>‡æˆ˜	ÓîTî?¬Ö~eq4ÑtÄ‡2íÊ‡²YW©'8€Ç¬ìcÖáyœz’‹‹ôY¢‘TàªœÖod×Vé%/K0Þá¨¥p+û/*ðË?ãg6ÇU|¼#‹ØQqÀÚ&J­œ@*È'±
½µMêŽôÔøËÔ±ÆÍ£íÁœ0í=(Òïî;ðja½³'TŸÝë XÒ¡Ý•C’ícŸ% ðÏ$îØÑìûÃ:&­„"%Íô%Ý$Á*Ž:R½éN×i]¨Qp’>ÆD>f§.Ùá.SdfjY”qaÒ–ºä²i-´Ú'®ý(ù™ç—@Éº|‘‚BD7‰XfânDq7ÓAV”àÕ¯èp¤8ÀrµâW±kŠèq¢†æR*¯9"ê’«rVÎâ¨ÑJAâ‘QÚçÅz”]H'(a™‘(Åš¢ñÂÑ‰šËæìÜzÁ¢êŸž£³ð«ê›øñ×øñï’7z'O¬®IiT'ÒŠ&JAfq¨4›>-€SÜvÈ?ô• æ(…h‡3¤?AbiTÝ/NAí&yHöñ²yuµã,?ýºQìÐÝšd¼£•ú…pzkN{S]¾<ÞåíUBœ
yÀÈ–oBˆØèÙ”lÄ^AmÒj¡Q®JcG(EÂÆ½™V¸|òƒÖ'p²½¢œW÷2Pe¸ó13ŒZé.èRZ£¿F^\PWO÷öþ|>6     0707010005c7f5000081b40000000000000000000000015ab21754000000d8000000660000000000000000000000000000003500000000root/usr/share/doc/opensvc/template.node.nsr.conf.gz  ‹      ­Q=oÂ0Ýý+NÊÀT²ƒ`dìÂX1û…XMípgø÷\h	•XýÆ»Óûº¦©	ÓPELtQ¸&]E(]ÝîÌ—†=˜Éæ7n—Ä~õR×Ã—ºü¨ˆ©qi´Ç³ÚÎ2	ã\Ã¿o<:[†ü2ø™"žd õþ]ÊºàhK'Dpp„Ê N/ö¹h´lÁÔ%¦Üƒc‘^+YPLd]).iü{dn¥·ŒÖ'×>[š9æÜb¶×¥j¯çÙFÏ’1wè¾	  0707010005c7f6000081b40000000000000000000000015ab21754000000e5000000660000000000000000000000000000003a00000000root/usr/share/doc/opensvc/template.node.packages.conf.gz ‹      ­Q»nÃ0Üý<dJ¬)C‚vìd2(ÒÙìZ
)¡ÍßWnk'EWÝÀ$îAÖuIT5ÄL´t)DW™®ìíªóöRÍ^Ü?<ÛÃCRL›Fäá¶ æ»ˆñA_G¬joz”Yˆ·äöÿÄ¢ÕiŒƒJ”Úª}®'(G ÿìH€q­3ôJ&°3ßA3‘˜¼qú—ŸÎúL­gŠ=h’ôaè64yÒ&:?íè<=5	7ÒkFc½i–c­kCîSÔŸ»¬}\{/ÌW_‘œD     0707010005c7f7000081b40000000000000000000000015ab21754000000e4000000660000000000000000000000000000003900000000root/usr/share/doc/opensvc/template.node.patches.conf.gz  ‹      ­Q1nÃ0Üý
<%ÖÔ!E;æƒªt®…:–CJhóûÒiâ$èª8ÄïX×%QÕT3Ýd“ë!¥è
BéÊfWí¯fÕ|êÎß‘ýæ®(:ôy€W1Ç".NöcÀ¢¶µƒÌBÄ8åÀðÿ'ÍCºhÌÆ˜•yÑzã©ðßŽLp¡ŽÞé#8¸‹Q%§»«=ý9Û#˜ºÈ”zP3eé/é44F².…8®i<|5·Ò[Fë£koq-,KCÎc²?kU]zoOçW¿®¬£  0707010005c7f8000081b40000000000000000000000015ab217540000021c000000660000000000000000000000000000003800000000root/usr/share/doc/opensvc/template.node.reboot.conf.gz   ‹      í”±ŽÛ0†÷<Y|ÉÞ"nh.]î¶¢(‰¶…Ø¢KÉ—äíKÉNâô.@x¸¡‚'‘þI~üíårÎ³XÂŒ'É	í˜ãlr3•›—Ýâû0ìEêtO§‹ýp-LM¶oHƒ3žD%îp×Ð¥ÚlB*¤üõNÈ¾ŽX*±oâµÁoìé,F ½“ÁÐ‘q¥3ð	*ò$Îä	U!Íxç‚[Š$P²@¬	VØG=°Ï– Mtì×ðD4ÙgdjÚX6›3©‹Îå"œ|ÄãZë¼Üm5w ³7ïø³ôÀ°!™¼¯TB³Œ¯e\€@”~™ê`YùGm¯åÊPÇO³l°‚)êéB† óU5c~'¯Pµ…Ðžà²[À
}rãD
›†d!º–}Ekx®‡þú.‰y"›âäµí¡Ó©Ùƒß´TÀÁÅ:e`4µ6¨øÄujæ½ª`H1ÑÅUÕèE:bÛ5´ž¨)¨ÄC›(ýVÔ•¯èä7{Tèµ6¬ ¶’«cÏæ¿™SñI›]œ—µ¬pÇÂ¼[ÿÓ/ã‡º¡ã5©mÌd‡#™>Ò}D¹†Ï",ºf½r•×ˆÍ(ÓÒ·pê[è»JPúpºÇt×°Ù«y~þ‡ûÜÇKœ~ìðÌxÊíØ¿Â	2WŽ  0707010005c7f9000081b40000000000000000000000015ab21754000000e3000000660000000000000000000000000000004000000000root/usr/share/doc/opensvc/template.node.rotate_root_pw.conf.gz   ‹      ­Q=OÃ0Ýý+NÊÐ‰f§‚‘‘¥#BÕÕ~!ÁgÎŽBÿ=6¥)ˆ¿ñîô¾®ëZÂtÔ•N%sÆAEò!.ÿ¦kˆB×¶;óô;ì³©Ž_qZDÝíU8ÙnžP–7QÛIV"'¬j<¥*DŠ÷Ù+ÜßÃÀó”¯%àB*Þ¡çeŠ°~ð–îéêíWÂÂl¹Øç¢ÈÊoÈPD) Í¹ªÍP\6ÄØf/aK{àÇkç¤}YÑ;±ý¥¬•j¤SÈü±-vëì®œ‰1Ÿ—„Ë   0707010005c7fa000081b40000000000000000000000015ab2175400000147000000660000000000000000000000000000003700000000root/usr/share/doc/opensvc/template.node.stats.conf.gz    ‹      ÍS±NÃ0Ýó'u(Hi›‰¡&ÄÈÒ¡Èµ/ÕÄ6wvKÿ;IÓV‰!7ØÒÝù½»÷äÙlÊÈf0a$8öÂótpF„›V»ì½[ö#Kƒîñt´¤Ö>–5ªÐ`,.&Œ$
KëÄ¶Á‘íU4œˆ€ð3hBõ³¢°¡ñ—‹b]‹â!žgL„¸RßÃ¥®´„gØ¡AÒ²[4±Œ›a=p‚D‹	*Kàk„¹\wÚÌÁX… ¤×Ö,aƒxåi`Zq-WÊÊÕY®eLðÉxñµŒìcîéfü_Pš“JÿÕ€7kðÏÊHkHWïÍþÆ’—~_Hòw2Šá;îÈw-¶eÈAºx8²2>
—ƒA¯ð_YÓgJ$ÊaÛX¹®Ô—á¾³c8ºq©v$Yö“9×¨   0707010005c7fb000081b40000000000000000000000015ab21754000000e3000000660000000000000000000000000000004200000000root/usr/share/doc/opensvc/template.node.stats_collection.conf.gz ‹      ­R=OÃ0Ýó+NÊÐ‰¦¬  #ªÐq~!&wŽÚþ{œ´MŒøw§÷e×uIT5ÄDg‰“½J’|ìÿIW™®lwÕËï°»jòüã>ª»»J›tpc@^ÞÄÔIø-`Q{æ`“)¾F¯p7-!]>Ýn.\ lzÚÙ ñ­z¤wôP/sÀL`’/¶çX4°ò'”Ú¨”:Ðê\Êé?¬¨ÄsIkÚ?žv4m¬cEã¢4—ª¦e`Ç>ñaÜ/³‡Ùzõ¨ÆYÃ   0707010005c7fc000081b40000000000000000000000015ab21754000000ee000000660000000000000000000000000000003900000000root/usr/share/doc/opensvc/template.node.stonith.conf.gz  ‹      ­Q»N1ìï+Vºš„54¡È¥Š(œõ\n…Ï>ü ò÷¬y%­§±äÏîŒû¾%ºž¢Ú¥¼ä©•]C¨]ÛîºýOØþú¹«Ë¾àô¢½9ÏäÙêýUCÔN‡Åþ± ¯E"ì?Âb4Ååóf›àñkÒ¥¿É´€e¦{:Â#
Wÿ†xñ>M_¡Õ;±Ã•Í³ñ–r ’Píð´yÈÐÄíR1Îª‰Æf¥Fx¼øî’ÅI•ppœ%ø•Ž»ÕBéŽÖñë\³uŸÈæÄ    0707010005c7fd000081b40000000000000000000000015ab21754000000d9000000660000000000000000000000000000003500000000root/usr/share/doc/opensvc/template.node.sym.conf.gz  ‹      ­Q=oÂ0Ýý+NÊÀÙA0vdaD®ýÒXv¸³Uòï{)%Têê7ÞÞ×5MM˜†*b¦“éZ“®"”®nwæ¬a/f¶ù‰é+±ß¾ÔÄõðe€.×1W".ö}À¢öf™…ˆq+áÿo<:[†ü2xLO2z?–2Â….8:Ð"8¸Ÿ„Ê N/N¿¹h´l¯È`êSîA«±H¯•¬(&².‡7tþ<²·Ò[Fë“kŸ--Ë@¦˜í}£Ú»e¶×³dÌ7D¹•0     0707010005c7fe000081b40000000000000000000000015ab21754000001a2000000660000000000000000000000000000003800000000root/usr/share/doc/opensvc/template.node.syslog.conf.gz   ‹      ÝSÁNÃ0½ï+,í:˜àG$ÎpCBâvi2b˜ÄÇã¤íViš8ÐÃ4KUÒÄyÏ~/™Ï§ŒÙ&ŒÇ[ö±žnÂP¸iµ›½tÍ¾Îr¥ï¸ýŠÉÝî	+cÉ“luóbÂÈª°óæqÇö`<cÞIøÑRBw¸ã°2­—}Î`Ã ‡ Õcê¶yƒ–*²p5LdKŠÁV3ž×8=4	!ÿK¼ÔÔ»Ýòj 9"’ÇOôg¢€5Á‘3‚ÜƒØDBÖxøL)&¿L
jQ¨¢ßÚú@ß†5m2›{¤^é
Ñå±3ážP$cêÒÀ8z<ÆJk¼ß‚#Îš0ÈÞ@ýj=º Ó-+WpQ³BÀ†d5TcD“‰;ûŠãeö§ÝëÈrªnû¨²å*ØÄ¤#£üó…Äì—ÕæüFà±‚€¤‚§.!èÝ(œ&áHïžFÝÖ›•ëZ:ü\fXü&^ÏzW8V–r;F0Ã©âTá[o÷ˆu9åT­»¹º>7—
Íªt6û_‘¸êõ    0707010005c7ff000081b40000000000000000000000015ab217540000010a000000660000000000000000000000000000003b00000000root/usr/share/doc/opensvc/template.node.sysreport.conf.gz    ‹      ­Q»nÃ0Üý<xibMR´c cÑ•([ˆ"¹¤ŒÄ¹m”]uƒ>îx§¶­‰¦…ŠXéd¦)rªCW™®nvÍ{1Ûªf=÷HË9²ÙßUEdfO¹¹©ˆ5ÑqÂOOEí½¬BÀô5;&ó¿cÈâìÓý@¥öJmÔ.¿7N‚løgF&ÒÎ:¯0P vúÛh&'¿ö`BÆ%b°‘!]I§ƒêäbx‚óèôyQ½øåS\¹0°î1ø¥­Ça Î)æn·…ÑÃö,ÜËˆL½‰º¿_î)YBÂË6ûx.µ—?A4WþÚcsf    0707010005c800000081b40000000000000000000000015ab21754000000dd000000660000000000000000000000000000003b00000000root/usr/share/doc/opensvc/template.node.vioserver.conf.gz    ‹      ­Q=OÃ0Ýý+NÊÐ‰fÑ±c—ŽˆÁØ/Õàwv ÿ¨‹ÔÕo¼;½¯ëº–05ÄJ·VÈiC×…®mwæ¥†}5«Ù3.Ÿ,þñ¦©n„ÏÊò¡!ÖbÔñlß&Tµ½t"ÁG¿ñlžÒÍà#®d âò»Ô.ÁÑŽNˆà~uåâø—‹f+ö©¼{`¡4‚6sÖ±³¡Èd]
·tþ=5«ô:ZAïÙõ×®*Sè%&ûµ-žêì¹œ±1ßÄ‚{      0707010005c801000081b40000000000000000000000015ab21754000000dc000000660000000000000000000000000000003500000000root/usr/share/doc/opensvc/template.node.vnx.conf.gz  ‹      ­Q=oÂ0Ýý+NÊÀéÜ
ÆŽ,ŒUc¿«ÁwvßK[«ßxwz_×45aªˆ‰î;^jÒU„ÒÕíÎ|hØæåÓLF¿p='ö¯w=q=| ËeEL¥ˆK£Ý˜ÕÞí “1N%0üóÆ£³eÈwƒÛq#©wðßRF¸ÐG: ‚ƒûM¨âôb÷Ÿ‹FËöˆ¦.1å´‹ôZÊ‚bò ërHqE;àá•E¸•Þ2ZŸ\{kiæ˜rÙ^Vªý6ÏÖz–Œù—~ýl  0707010005c802000081b40000000000000000000000015ab21754000000de000000660000000000000000000000000000003900000000root/usr/share/doc/opensvc/template.node.xtremio.conf.gz  ‹      ­Q=oÂ0Ýý+NÊÀTÒ¹UYQ×~i,‚/ÜÙ*üû:mH¬~ãÝé}]ÓÔ„i¨"&ºKœ×¢«ˆBW·;s˜Ã6¯Ÿf2{Äõ‡Å¿Ý5Õõðy@Y¾TÄTŒ:í×€Emk„HpÎAàŸ7ÍCºÜqÄT¼Cþ—:Â….8ÚÐ7"$¸¿„…A]¹ØÏ¹h´bOHêX(õ Õ˜µŸ‹YQd².ŽkÚ/Í*­öVÐzví­©…gè5&{Yý÷eöQÎØ˜_ZèªÞ    0707010005c803000081b40000000000000000000000015ab2175400002078000000660000000000000000000000000000003c00000000root/usr/share/doc/opensvc/template.service.DEFAULT.conf.gz   ‹      í]msä¶‘þž_Ás.7»Îh$­Ô•²Ú²üvñeuyåøƒË'qHÌ"’`R£Éùþûõ@œwkFª­5C‚ žîFwã·¿=äÏo~ð›ûü‹/¯¾{{}¨æøÍvì~óƒyÙƒ]½‹¹*â‹æ‰‰ŠînJ™
U•pÁÉpht¤òpœˆú‰_†‰øM!þQÉBÄËßä…º—ZªLfÓï›XLÂ*)›îÿñ?–ÙL²³È<'á$xÌDÛNˆ Þ[ü½ÎE$'2‚k¦"ƒ{#¼,RÙ½(œÖãªKè<\GðÝUýq òBhìé0HäçéGgz0
®g"HÃ™Vi0eàUåL8”	#jA€ÿ¦á›õ}”N€ï¦¯UN—*èb!¡/Ð’ÔNSyX„©(E1‚îþÉ…5¸ÄqZƒŠâ&Eª²›¼÷a)ž‚×Eu8¼.¡µ †ç­Ìª‡a àB¾üÍ1	2V
ZòùñÕ$Ð Wüü!á÷2Á\&IPAçJy¡K‘àÛY#`°p0eT ûšúŽÔ€¶à5ØøÐ*‚»íq2•ÐP8ñÓ4Ñc%êÀ“Mã¸†@“D<`iX,ºÇ›‰,t	ÃÃ[Oüog€ÉŸXä"‹uóøRå*QÓ< øa€#9øÑ#Óµé^I¦ÔM½È"x7ê®‚_wzƒ%•FZ°`Ñ
¯æqp½W ¬!&¢ª$ÐCàf.lsÁ	ìÍTÄENŸ¼$îx-\î4„ëdO‘ßüÚècÇê91Èôi‰l×--UÈ(ûž7Û¸T…e’»\2pHµÔÜJb­Ü5Ü*T’ŒÃè®sœ²ýÂk–°=«G’Pe}TEKtRŽe)â¡-4o™e×= ž,u0“É	,fÜÀ¿¥ ôëG\òë¯Áo¨ôŽf"®’îi)ŸœN]mMq;Î<D¸“Š•T;Z£à½ z§¤mœ‚
pêŒ¤ý1:j}Ê‰2| ˜Zãhá­+RiÞ]¨ÎÎ.ÎÎNÎþ¿?ùèçÇ†+‘ØtPT»a†HµÖöU˜y0 bí[ƒ¨…²TEwüäè˜é§*Ð¼íXr¦µ‡Ÿ¦ÚÚ¥Nh™MT‡±: ¿,© ¯ôìpµ@¸Ö›Üøôî‚õä’1›ÈiíqI%ù[PUA0"PD´4ùØÖÏµhzøì.2Q¡î0¬7/yþêh°Z¥«ž”úž¢×z±5è…sÝ9Ìþª²Ã™
- `<‚(‘Æ¤Ãw&U’äa9¡½€~êjÊ?]æ€x€†KòE9Hº*‹Ñ@Ô¾¹ºþ3A…w]Âwj(`ª‰ìà„23,(Ñ8—åŒ÷êû÷A˜K;¾vø`œm‡6;‚ïÎõ±æÐ|¡*0ZïAÉƒ'Ê’Ç) £Z“?„œ³°
ÅAŸ‡Yé¾®ÀR`I0rá?B{0ŸÔ3_O‚öG»
øx=SU£îI?Ïl–Cœ¡NQûél3‰]ï/|
´8Ei;–Ù)zr>úÏÍ‰{Žü9böVR%^Í•x;Ynâ°obYô„iã:Æf¡6CuR.rÒwËPÂmŽv„¤!0Í(;ÓÖR9ôš$ÿÜe?8±û;Ò°Š—îþ¡Æ/Klð5²ã>,Þœšôék}eÀ£7ù^=¤Ž¡[Avš¸I)_P÷x8€ûã…÷f7KÓ0“ÿdýÞÛÑÌ<Z£3˜*2ÒAÈgv»DEabð7·˜yÈ[]ÜŸÂ+œŸâ‡ævÛ‹i÷Ã~<¦YÏñðHN×ÀP*¡ÍmÃã*Gç¬áð»\dïÿöNÝÚ[7„FŽ"êÈÊFêQp§ —ƒ(ÌÈù-Ê"t.ðlHvg»ÜýRá²¦ î‡0¨™1š.ÄN”ã‚…üÉ‰Ì¶yŒ&hhß§S©K º’»´ÈDà2`»™¸ziÏÛ]xK†N1­R‘•¸TjÍûèÉ Z»ë…[TÙ¢"ñç¯ÝÞ}W`ï˜û–vj¯‰¥ºYÝ§­´¢¯)UPx FªIm¦‰©ððÚ%ØÎ00–©+)—‹"EymèÆ1éaâðÞ}UgcÖþ kkƒÃ da¢`NNÂªTâ²¸¡åFç|Ö9âKYøõ<Ak¹½ÐzPrTu	À£T©Á+(ŒÞŠ8#+U‚+`‚è6ŽÑ2¼ÃÕB*x…±ñÍÕšÝØóVìg¸'ÈÙ;-"VRK2Y©-7"gÃ]|Hj Ulžk?Å•¤îE­èÌ(É±ß+Üç¨J«aÃ+ÀÈ`oê¶ØµGmô9ÌBPwÃ	é×Ý#"ŒÑ‹~|yT1ŠF†ÃÑ+°Od)t¢î)P’ 	“âŒ	È…%¶±YÛá*6-Fõ“$jNhÛí²à./ ŠPõ tí lD8+eóQa¶ëWuË{ûó¡v±ÛŽ·V“²‡º33ÚZd×ÑÝ¾½Kºh¨rÇ2F£@NaíÜ¼(Ò`n°êÏO¥Çðö€K†]¹ÓËŠ®É
Dm2ˆD{J¡™šßÄR#ÐqçHtè¨4;ê6”Û~j:°SØÚ{£‰‚UdâÍð:¾3+žd%UÜ(­ èz—	œB´ÿ®1lŠÿVU	j%ƒî¸%RÍ†evçI(L18&Öö€÷¤m‘Ûï.üèAÍS­st³ä¸Î€ÑÅ¤HH)#×R…¶ýf¬†nÎ[”F©wÎ‚×—Aúrd§)ñÆiŒDý@†q*³ºmÈ†r>ðß?E˜MYY©ƒs/›q]'@Lý‹oª¬˜ô0EÅÉèûÉŒ
R'lÝc^*j·±NpWÆ®6€k˜
×có:{3ª=ÛC¤V*Œ×®Ä0é2xM¸¼	²*aqíâÊUILvgVÐ›-Lº¤¸¡Þuòú›§ŒO¾ªJ•†¥„K6T™RUŒÌl^Y`>ãŸ‚î°LÌ]AO£ÏÜp 3x rü†k°ÙýS@vØ,¹×+„%¼ÂA“¢Úâýó/þ’ýóo¿ÁõüÝ·ðû«¿^Ãïo¾½¢ßŸÓïÿÆßüÇ·_|¿ß_ÿü¾þo»~7|wuÝÒ*1­	ïqºÑÇ…ª¦³Ò÷žƒ¯;ÆUÉ/ûÃìŠPï4àö!4X/ÈI†/£eÉÕ/É{ë4f=rs]+—v¨‰að/&4,Ã°†e™ºÉ!Ò´Ì±*ž$1óyK‰ƒè‹MšÃ …Ç`XoQZžrè“–žO•|Ýî§½2RiJ{ °ÀÙ*þ’ü?"8
Þ«TpÓ¼¶¸+î"á@È)6±"Vë*Ï•5l’ôØ”()ôÃËQó2NCƒ	ŽÇ Æ“ÀurÐ&ÓÀ²“9îCc³‡èP>qÚªÃãPK”í–¶	KUD3vT3J3uLúÎB /›­?™G-Õ%˜(ÓuÎÐ×X#µ8kikÖFËq^#„Íj94¶.•^Ò¦Q9rŸKß<;Áö×Zõs•ÊFËÝÅø(¨[ÉA¬A9Y]Œ°·§’0¢‚ Äœöj}`û…o®Ù†Zq0Â´xËÃ(ø7ñZ½å¦èb†Z¥³:aËá› fB{¯öÚ¿äºQÿ5Ç ØÒûiJ§sÞÛlzíRjù?*å^Hí½,üÆ8ØQð­Ë0©éEiøPæ‘u„Që=	Ì«‰÷ÓKdïº­<ûRÝÓ˜Wš×ÇÎc@$
–ð~Š¢a&'(tŽHâDª¾ó7p—¦v¨Æ§Œpé‡îƒØ$NÄ¤<ñd°0¼‡9KÅºÉ«›‰³üDR¶ÝÙfÍ¯<×¾’}(.bø4É=³é÷Ä‚l¨àCW¤à0\˜Ñh2‹yÀÀÎ¾ÃÉjÄœí46‘Vœú”V )¬Q‰Ýãá½ íUh² Ä$[¼ÖÄ‰ˆpVÞ‹„¤qÝ)°ú×!šZDœømgrŠÓ„>"3¤~a_“0¯¼àˆvôAâÈÓŽ¦­‡:&é>¡±‚DJI³Ðó0çôÙôK»ksó¬{³{1"ö9,eT»Ò”ÿYèÛ nÕ:ÍÐ2£t›nåÁ®p8vµ1[*©ñvq€´Á-pMC>â ÷\Up…˜)ôucüˆ+Â¬‚µÖ¬ý@}VGGK­Ô†ŽHÇ"nœªN[Žºñž5Y¸I¿å'(³ÖÝÞ^^ÞÞáßÃ¦noßðoÌ¯Í¿ðÐâ|ˆW/ë~à~¼½uë´/g© ud©r+¿çNøŸSþçCóåéi»s¿óŒe†¾§vãÿÁ÷ÿÄÿüÝä›‘™˜²ÞfoÅ¾½¸ÚO`–ùðúÂŸ£
º÷[1ÉWê‘êØ(é˜š#9‹ú*§1ŸâÚ^oBúðãX•›5h™3OÆ\ÿþâƒÿE£¯íÿ>¸¼ü {5‚Èô}4R|°vßÄW¤º§J.•jçò;zR¿6ZdãÜ4Ú ‹ãï6• FÁ§‚·ÞPÉ¤;ÜY•ˆÂˆ@\7‘iu‚ÞÐD1»9{VõV9|éI"\—T›¦úH£z_k3º|¿|÷ˆóºÁ¥)ÊòÙ•M¦ÜÕŽlºƒqx6……k®¡‚¶†NÁhRa¦ƒ*Kd*­Üòyƒ~»Õ@l¢]”W70[nÊ¨
3•<É†ïaéwÈÊj‡eÚgÌ¡“¹Œ…µ0`%œ
X€PDÍg2šq¨î²yDž¾±	É]ì—ÀÃ,Ò­p£ºße¼ÏÏºˆxHbÄA<óeÔ=³ð·¾uC\\ÎÖ´ý|Ö= ŸÜ7¿ÆQ¸†+?xì›°Èî¥ªt²Àú)m«h˜€‡[ÿl–xKÄJ³Ë¼áçlñs{î`E1‹îÖÇM¾ÀØ/Ã·&Ìô*TªŒíêeQØá]·Ò/</{«pEF°B{ç(ø~E,Úrf‚}ÐæìÐIºè496²¸^¶döÛÑ¢ò5Â|À_eÒÙ+vsàu‰¯ž³ë¡6À¸k}b3™Ä¹2{lm¬æÙÞè’*¶7º»ðr¼GçÀý…„r,&ßÀNÑZ'e²÷™ìvS²T¼`Ò(o¡Of¶Ò'æ¡ïÊ–¢Œª%
½ÎQko™‚¶àk-†ó˜ƒØ¸èZÎòf¡;›j»wó;¯/êjbö»…ð1‹é`mË*g=eÍ‹¥èÖ:c9ÞÈi¥ŒùfsNc•JnÆªG³BÕ‰ô&~3øÝÀi¤Ž¶•W©AÞ8ZëM³¥W;‡ë/07¯˜‘JLx=\}De;¼X$€–G	00hê»sK›5Ú:Ýü}âË¶h‡.«I²IY>EkÿA±`óaêú­Än§n<eµRZfz\d1ÿÛ©2²Ÿ"¶2ºJˆ^®Á÷lB¯É§à1'-Ü†Jâ0ÄM½©OÜ]M&òÁD<ÖŸ®§L˜çÝãÊëz}gÊÈà”“…30ËAÉ’ãºpaÃŒ¡¹ÈõgÈ„£8êØx’ãa
TL$Ê(TÈÜÛ)ó;!r¬0à
 =ÃH¿Ó¨©¦Xžo¯%†q‘Œû9–ðg‘ä”à^ànx•v£PI­`9˜¢C…»1ŽTæFë`(]š…5§f¦è¶2ûÂ\…½1°fba·ÿ–*]QL}‹b¶t°	)Ù˜Ì %ÇÊvN3èdrÉåÌa1˜ö´ÈÅÉ‚XóÀ]íÅšòØdŠË`Ù1Ìy|I }´®‚ÅµÏÁfË…ÅRßi$+]@ÓB†–%ÅrQ17ó‹x†:å<wùLT1ßNýtäz‚¡›œÕéJ'óU0YJà]K™Zs•ÔŽ¥äoUr…ÊI¡RXÏZ1]¦nÅ­ò
ÞÜÔlqYŽ³vZ4sbS¬õŠ ëæÆm.çñœ¶,ÿÅ%ìª¨é\ˆøæ.×Þ¤ú¦æ\Æ€%*ÙùõD2éP T”ç‰žP…¨·3‚™?Î^dtæ–&¡ö<	 ÷QŽOõÆ:Á¸ËS é€‡,¼ë™[ïMVž{ŽE.`Z•s³”àëª43•_1*9
Œ{+g”.„W™JÁNfŽ€Ã(u.³¤ï$ù#ÁìÂüWî`ã*"Hï®Éš±õDñ)ÔÇ¨æÊ&@Hú]2L"…ŸSXA¢¥¹ÉnŒyÃ
Ã¸ù‰tN€`SÐºÝX²ïu!ã7§ø7Ô»×Ægej%[*âvçúúÈü^á¿WiûñÇOVÜwó¨&æ5/\‚‹Yìî`“ˆ8Ûìó‰ËÞ™[Æ^—9FÙ~ÏÇ‡Í³Qð~f}
Tâ+Šà]X1tÚZ@+,þ"sJˆYj)ÐH‹2Õ»vÊlƒåê;§™	B¦ÞÂ 5Oµ3sy6xÓ>©5%ë“ÜÚbaBga~c&v÷XyôÕp©äWãðrÊ]á‘#fŸ¦NšÅqPKÝ:‡ö£ƒÖzK”:B5h3ÁK)V‡Ë“jJÂµ9iLyÅ‘>ÖrqE
ýbYÉÅ=ƒ!š¦Ð¡)\ghÖXÂgšˆwz¯È§(£S¬Ê1ŠOá²¼n¤gkI¯Õ³è0,
'ä:²Ü!ÍÛ,e‡áÏ\µ¸>ŽÌ‘ƒ+L•Àk¹¢p…ÜBà“=œ…»Ð9³÷X>Ž+&{œ‘*N½3jíªm»Î¿Ú”9Æ–aÉÿ¸÷hJÈò3ÑÌŠ¡{\%(*B=k-¸ÂaŠàu»X HukßúÜ:ç›GØsWñ«¢	úB<Œ‚‰vSÐ«TUYi£!<$¤]RT+ŠpQ‹®‡‘XV»ÜPR¦gÝF_K!0U5ø‰±ÇÒ25ŠË%_yÐÑUÞq¹Gi€¹Ý]¡œ" Ÿ—1Så¦öÄ×Îlš%ŒèöUÁ%‘1ÐÍl±.ÁÏÝ_ëO%z“O»·6=ËÂQßiÁØhín¾` 7è(ø¬ù†€E 1û£T¤
+c&1!Ê+X¸pÂç´‡bœ`o"÷Aä‡åÔæÄåS}‰ü'–ûÕŠ,P÷cã¢#¾É‚Ý_Ð+ö×Z–l)N•O1Øµ{;¹EÈZŠGmR±ÏþªW\þ­N0eâ ý%³˜ËÈÑ
SÆÅáþäU{¶¹äoÿ\0*¸Î†çÃWøÁÙÉ+Z''Œ>‡Öƒ	$ìÁ<˜<ÇØÁòkçsv1ï	.â¶\K§³°zˆ7C¼c*Á_D‘‰d¹fŸ¶ûnvS>>?{õ1ÏŽnÅ–ÝšW'c:ØªV"‡ÈtÕ
òˆ•0z„œ‹D®Bé¿Ï ` XA0¿˜4‰‘ÕW01V”7†ñ€ÜL¨Éaœœ}®ŠQç§7BÅP
Ø÷‡ómì³E&zòí$_èp.F* — ‚Š•
)³ß”0hJ™J”,‚x#TÉß9`º¨ ¹àmÙúk•T¶*‡­ç_—¶ttÚªƒ9ZÆè­Ìª‡ ¢;ô089ogÆ±žëÕ@ÂÚ˜ß×êßH™uÚ\*£Bi”Ë*Ø!páèã†q“*8ûÝ'¸#ÃmÁ_”Nàšle¼æ•w…kðÍ_ëržˆŒ”çÖ7¯¨7J¥7¨xêI6i:Lþ%ïU0IÂiðâWÁó—3ÎÇú`¹
[Ìˆ¹óŽksš5öN&‰—!Êg§0_i»‡Û³üìå¼Ô&&=,K*Æ…N¢9e	¼¤Â³H®ƒ$¬Åÿ–i*bRV	u¤ŽO÷îk§!î$KløÆüMeLçà6»îH³ÉnÞ˜iy,LÏt5Ö¯ñ'³O±=²ÒvµÎG>á²vSn´¹úÉö]ìÖ`žyÔþ‚}½Ëª¿¤W¾ð6òÆ0m_UåÉ»É‰7Œêmö1¡ŸQ¨EKø¡ú€‡ÙÀPºÜ°ÛÓ°äŸÑÏv Q›ãñ#½ê¹7EV	@S±©U³`Ú£ˆ½eÛÅÍ¹ã2Ø°6½¹ï'õ³˜Ô¿'Ð‘g6oÍÖO6Ú—ÉÁšÒ[Ü=wþ[bÝ»­œ­"aœÜIu3r:ëéuð)E¹}õ1¹î(ø‚yQï£ŸŸ‘Ê{ Œë°¢~YúÞàë¢E:ï:d¯ºsˆ5šcZ×ö 5/Óºú“´2Žã±›_P&©N]¹±Æú³U¾Ì¶¢g›ÜPu¢;xºß/r‰Í±ÀWÅX–)
;‡6’»„Å¡Ù“á.xQ3Í©f‡¯î´f*üÖíÊl’TTØâPé—×Ø†æ<cëºòóAx¯d¬©€FD Ë§TˆËnÚ
UmCE9„hP|Y} &FaŒíiÈ'„tß¶£KÀÝ“‡‘ù.›`áfþVÔ\ßÇ#Å±¼ôŸ›ƒçÍäïÜÈ>ÏÈù+Š€[µk6G„œ@©+Iáfï¶Âþ”3™ÝÑ›Mp›ŠT7îD Éíoòn­Âý¯Œ¿úíÓêb?|´ÅÞ{9N=«J
žtÐÿªÔV^Rªí<\`"–)Áx{”Ë2²ßÕxÉ1)ó™Jüôê:Ñ×öqäöwÉº òœMª½Û”“tos=m(”ë¨*DYG«h,Áy¥ñÅ­®bwAÓ÷Q:-‚ÔÑÂ¶W·¼F±ªwó‹[ó†#û=¼»ëÐ¬ÍkÑªeb(hñB?rÓ‘æñN;MGØkvÛŒŠ9|ù'NQ5Ž±m5és>ñ¶sÓ&ù
B}Ç´]d‘ÙxžùJÁ¶)o’rRú{MðÈ†òeK3×³}¥‡ß4‡Íy¬ÃaÄÙðËÍ%
> ÁœûŒI?#¡üHåVÎÇÇUšcÞ¤`o¬KÚI£Áq…tC2ˆ±¶µÆl¸Q·ˆú<¹xÅ¼)‰aÑ¢#ž²ÞÌ€»"¯›*4²%í¼Œ\j–Â¦MNšõ•¯*pˆ¼'ÒùN×d­±®‹ÅGÏrÐˆ‘ë-9©€Æ‹ŸªIæR?æ¹Ñ¼Â3mhË7æ¾3Xs/1n#§ËhòprLìÑ‹”Áš,°g\[F£`D‹às.Ãé¿"?º™Pæ/L÷Ü®-M&;>[{ÍÐÎM˜g"÷Þ„Û†´ålÑ<½pk^CÁVe.ö§VV¾7ñ††k¶¡Ö€5×+®ÛÔ'ÉëÚ”ò@Pg^;µ–@9ì˜ž/ZiÕ”|`"pm¿7GÊùoÔ³êiXåŒú2{2<†î36s€eSçàž«°ÁŸî=€P±Æ»Q÷0ÈÚž}3F¿yŒq7”Y‰…›17Î`iêiÈ:ÞýÔG™ÐÑGï¯þjV$»©R÷„LIŒÎºJ’U­™md%¯êþúÇR9ž± ¨RE—ÂÁbsî,Ý¼õ`Â(|’j*ÿz,u†nR2ç@Œ¸m[Ý¦f²ÍrÊâ˜{XÇôò:gU]ÈusÌ	í@ÁµhŽ3n
JÙ ó¦ÏvÍIÜty¥;^B“ÍnCSnSß&i5
¾(
<­ÃïGy×E•—Ž«óƒüõiƒ™’ÖzôöF3 ^-èÔ=t˜xmÑuåî1íö™t=nŸr‡€ÍÃbûtëf´Ã3šjf[â1ÓÌÆ7l›b=V›^…ªÆ`—i¥ºg~<«YUoìýìI¥òæTÔ£¦Ô£p²lŠÊßÁ¨Üô¦ÙÓšfk[‡È®8öVÚ“Zi;à¸—æÁß£ø4Ûn“ñgMÅÂ'±Ývœ†?gö¦ÁqÍ¸Ý&ßÆuÛQ-º'Ü^°õÆÝ‘»]§ÚîVCoçÙÎÛyžmÃÌÑ@oÌv/±ç8ˆÍë“ªNêó©Ü#SbiRÁ;¶a	ÀÄêšÁÀéAm.\5Q¢þÔíÍx* üâ5Å3¿ŽF£—\ÿæ¨V•báøxÉ‡ºÄp5<ÚÐÔ•ñ[£<S Èåè¹­vhJM‰‡¨ôüyþt„={q‡ô€ž7GåñNœYÃ§¹}8³’)KmÙ[‡ù¢òž.G¦‹ÊŸ;[l\˜Ÿ7ÐÍsŸ“ùàŽbÍ•ýƒŠšvvÙ'ê¡;€ÑŒø#÷Œv…ÎÂ½õ÷Ø)cøÈ-YÛÊŽÓ­íÑ“íq˜¹Hì8Ñ0ç›+Êõ¸=f²5ãh~î
gZÚuÒõ dâ=¿6"»O@ø§‡ïñó‡ñ Ó°Ú}öõèbò=¼;N½*¹ŠoÞc¦^=ŒzÜÐ®S¯GïSïÑàµðØg[¶7ÑŸÈ»²ãíŽFzk“°Gñi-»nÚîbïsy"ŸËÓp«¿ô¿ãº_ö™€{à×{bžØ³Ç4ÜÉ”_åÓ8eö™’{bÙûgžÔ?³ß¤Üjã¯Æ¿òè®š=gä@ö^›'õÚì1!÷³ü{Î“:pö™» ÙØ—}(ÕqC©ê~Þ!›+±)ÏXG=yŽOæŸ‹ç4·'uö‰Åk“aqX@õÜ9>wx¤; yZ”ØÆ q!Â»ž@Ç'tøãb}Ødéùs|þðHw€@-JìÀ tCõz
áPwƒC)¶ä%À+wŽ6ÏÈÿÃ÷¸Àhl`{”JÒcœH«¢R¶¢äy²z´Ž7§vó%îW?½Ž:½vôn.èõœ£ê9ðáóN·t)@ZÍÿ/y^´^	 0707010005c804000081b40000000000000000000000015ab2175400000de2000000660000000000000000000000000000003800000000root/usr/share/doc/opensvc/template.service.app.conf.gz   ‹      í]mÛ6þž_A (œà¼ºÝ ›ÍâöÚú’’^?Å.-Ñ6a‰TIÊŽ÷ão†/eg-9±·ëTþøEçyf8CŽ´OŸòõä)9àÅÑ²<¤¸¾@Üam÷ä7ìÓóßŸ ¢¶^I•]6×Ó©â¥ŸÎøB“èT–t’³úZïUÅðÅþ¨¸b¯i®í/¥’K®¹\Ì.[¿dlJ«Ü4zÿ,…ý‹9SÜP‘úåŒNÉ5™3š-!3å~×%Kù”§pÌŒ	87µ†+èŽx]å9)©™#‰™3Ë”œV"…ëxc%ä­"Üh2¡š	Z0Â§öØ)Ïá½Ž1—Ú°´´¿_éeŠÇ_'™½FBÞÏ¹öRIQiChš2xO5¡jvŽgEÒhjø’¢@|'…&Lm¨2d*•{7†ÿdé?Ë?RSiü"JcPm*í¡ø&[¼ôê¼‚oå“{XcxÁdõ§ØPŠ%S‘ü¬R‘iqêWÊ:Wáçk2aðƒ#Òœ*Ð{›kbB#È¦”ç•b	ù–
@$\\ñŒÁ‡5¹\¬.ï®ÜY×· »„¼™!#ÀÂ‰Ç€+öÂ0&CVIÍXnX¾¶Zoiž¡˜©”H"q7á$uÊ¥²*s êŠƒ3Yåd‰ºÑü•HïPŒ@õ‰òÆÃÔL-yÊ(¨¢-Džà«9Œ„·”ÉxfÅÔ=yyE.þq~ƒ­ÈÛÇGç±Ãn›ßÓÓ0z–2àrêxí(Ó¦1*±8†µŽø³ZØÞÝÇôØöàüÓã˜ºç·™{Á^œo¹‹ù²ˆÿÄ‡Iøyß {º´«õg}3àÝ¤·ÙÎÀúã³ÞúaißÂöTyïìÖ—ø­!ïf>æõñO|[H=(ïcdO•öÖj}Y¸Gvrl¯9ôªÂrIØ–V†‘Ñ•+ñ¯™Fgé	p¹$™ÅqŽtGi[·>øJÏsö¤µ5ôº¹2,•EAEV_¥ÉWC7ä™ê b7ˆ²ôÖ	g£c÷B°9/d`]0ÚÉjÀ±‡3šJoÄ;¶¤yE7ðŒ@lýØ¨¶zì¡rêt£æœÜÜR}rH;ÉÐü¿möäe0ÑîÅUwT
ùÆ`ÊS¢‘z³¬[îÈ}k{–UsfÔÐÁžÜD#õ1æÔ8Xs§5­•ú˜³`E.O0ßxhƒz;õ1©¸³9X´Ã¢ÎL½
£H{vÙ­ÔÇœJIg—1ÁF}L	ùÿ;;i­ÔÇœKˆ²ƒ5;¬‰FÚmL\ø;93>øZÉ.DKÑA­c÷XŠÏk³]+åŒ/,Vïá	+™Èt#8UŒv[Î°Cì7Ôù÷Ò7y.Waý5N\^Ÿp‹B¾¶°ø«±Œ yò-üK¨jm«Ö‡P±IÐ–(*fŒ\’óñÅø~q~ö±ô8œøÅý`‚ß`L0¯Tkp[·Zý}0ï.âÖ	.0àVÏAü ñnˆ?2ÃqaØ·ð"ø`J°<(Kp›EI¥Y6&«9Oçð¾¢9ð ¾¾8ñ5qöOÈ›ö–"@s6‘°Fsã–iÇ„	])¦Ç2	Á=4¸)¤ý•
ÒÞ´× rŠR	†}Y©·+qs0ñ9)|#7Þ$v©8#¼(H”–ÆvÀ8=tÒO)`ß7]ìû£’_Ú2Ó1ãæ‘"veAeˆ]$PŸ”Z›Cš)Y•ØÃüUœè&¬ ¹`´n»x¢eŽé€ã/HÒÌ„þæ–ÀHVI-˜a*!¯!±´(s6FýÈEõ¤ö=&gõ¨ ¯67ÄkcÂ³¿”Àz¾ÜÒÅˆzˆª˜ ý`øO•Ô4Ó±	"qŒ‚ãÝ¸ÌòÍùWÿ„c¼,ø„Bð´¡vºà1/ZGDÒÌJºccÎ["#åôÝ3ê­”Å-(l”Ìòï$ÏÈ{C¦9‘gç8^<Ç h \¢4~•qíÞ#wÞV1÷sì‚çy«Û&¨ç«íÖpr2þ|LÕíäScXQ$(©«ÂEàH”ŸÞmH†+¯íTb(¾çEÁ2Nm»†U$Ã>o«äÛŸ"ANI±áÿ=8(§…yÌÇÀdk?˜ Õ˜Às4ÓÕD¯5ã%ŽÂ[	f˜­ [F.’&ŸˆY‹­·Î=°{_v‡Ê¹¦ö÷vÒ×}fý­¼òëÀÉÚ0ýÜ“¨2go§g- ýÌŠÏÀ»Ü-)Õl#øaúPR”Š¹aÑvSþ¹}u°C¯hYrÁ¾´e½?+ ¾c¶«¶ªŸ0CXkÚŽq‹ÎxE¾Þ…ÚrpêGáÔCÈŽíÙVRseŸ}áÝk$ÂQ+§^ìÿXË8"`•Ó&ù‚ËÛã³ù@¯ƒ„Ù¿7oaˆ9µ¹´³nBþƒ`^fÅ˜ |lÊ{ %¾‚XQÖþîñÑ²9ï}È¥N²Kóü`kº=Ñzßtô‚Ã‚/®]£®oÃÄw¶f'ÂöìêÐ¬<…d×{'nK§öeWý0V#U¼†D¾"÷‚fèìô&e(QŸýöûócâ—Ø¨9Ü:ÜÔZ,!7jÂ¢|÷HâÚâáÂ¡ow*˜¸Ñ/ ¬]Ï5m´i	y'¡º¨år1Æs„9SàÞq3asºä ®UŒ“‘îú#B—’C¹LÅÚ¶2’B,Ÿ!“Üq9¤d]fŒbB#p0³˜ë*Ä¾ð¿Sw&jûªFæ¾`¨@ØéEˆÃÄü˜M0ñÎÜKíì¸šÑœç,åZríÛÝjÞùOÎ²‡ÝS#è/Ï€ãabEmÉÿšël9úDJ¸”ØpöL®D0*¸t’Ø6·¸pÉ â)Ïê‡4P„îÒ-4ÚDÏe•g6R”v"n1 SÂ	“Ë,BhïØŽóýF‚?}gÈMÜðZKë>“·@y8õ¼2HxwçÑ!^rÂùŠ®5.%ÂKîî ‚ÞÝ%®¨ƒÌï»ï_ßüòã{wßÏj.óÖ®Ÿ¿&
:&±¾[ÕÅ\
Yß²-²XTÓø¯m YýŠx¡ÊÝ®$O±¦=/Ó"»¼ÓU«s]/Ób¦È™nž‰´ºs[aÂ´»¹È0	¿Ãâ]‡fnl†eg-î†d'/\Gni.Éiq«fwUÜÛ0»EÞ°0Öámá©“s7ëv­€ê…£íZ¤~OàqäK˜0[›ºMÒ¶MŒp½wd["FÀ#Ï_ðÎMÏmÕ¾XåVe‡É¤ªs8ÓŸþˆ¸\ÂdÙS± Æà°ÄÕÛ_4›V¹5tCéœ/€wUQÚgË¸ÕØ˜´Ó&ƒ³\$2ö€íþ›çD6YŸQ'o\w15ž¿Ç¸/üM7hðúþH`IFY6ÑˆèÈ]?ðSó £‰Mè[©Â„¹*egÔ€ú×W`÷9¬ÅìÒÞº.y
ƒâå,’fp×X6VO­_	)Îì–o†ás–t&($Dh8Ä/Er
Y	ãÊ"Œ½èSlœÀó*åkÔlŠsK’U–—ä;¨]ÌæÝ¥‡òc¾ôêÅªm9S°OWWS`èÉ9Lö¨üóÀÞÆh»¿¼àe;ïÜêAÚÀV‰ƒðª“•_=EZ¦¤¥Ì9f:«*µ«`0LwOŒ‹¼í@[Pc]àÑ’Sg	ÆJÜ”åÚÖýñê-5®ìmôvú>NµG4°êaXY}›=>‡ñ5vsÀÅ¦“ƒÿqÎÂ{î‚J(Þ}º7Ççõå
¶†É&õûn`ê ¤d˜Ü½X†W=#]Ž·œAúÈsƒz¼»ùÙÏHaS¥ÖÄ–’Øu“ç“æ·	üMÁtsþóÏÌµD©ØaSÑ­v0·ŒàOî˜þ˜Hé_ý.ý#±Ô­àzn ô½V.KÃŽöa³VÇ†žaHÝšeQjãrÌF,®ÔV¨,²¼EUßsbw ,Ár¹nžiJ¥ìáŠb{½îÌ‰ÝžòLw¬‡Þ4öá™£H‡Ð‰ï·wZákW´JÈ÷JI¥ý= vI©ª4QóÙIÝKÈ¥Ôf@oôèÔm¼<:Ð¨èàx›c¼”»‡ÛíãtnŸïr‡€­…E·»f·Ã#rµÖc§>ÍÍBC—‹X}ž{}.T5}Üêö¨¼*~¢×§9•}X·O@}–K}NÝ@M°1¬r;”f[šÝÜ}ˆôÅq¨Ò´Jëã^uZþÅ‡)Øú9ã'¹â áƒÔn=ÝðSœp(Ž[Æõs¾ÅAê¶£Vt=n/Ø†âîÈÅ]_Wë_5uÞ‘ë¼Þ~Ö…Y”ÞzžÞ=ÇAl5ç†é’¦ìL3|v¶£…ÛyðQÜõ  Žs;¶þÏö©Jˆöíô”Œ"Kêrá¦doÔwOÜÚÜŒ¿R<»~veû™¯ÇI’<·Ï°Ÿ]W«,¸±OÉUåXlW«ÊQx®L[š½OÇ? .ÈRz¥õfkJM	 QgßÛÀŸ£òçDØ³wÜŸ+xsLÞø?îÑƒ3÷0%·g>Ê”-YÑ_­ëÅYt92]ðÏˆ<n¶D$°dù?¼Zñgz    0707010005c805000081b40000000000000000000000015ab2175400000e39000000660000000000000000000000000000004500000000root/usr/share/doc/opensvc/template.service.container.amazon.conf.gz  ‹      í]moã6þ¾¿‚À¢ð.Îq“½öKÚ]\®Ý‹k»ì¶÷¡(Z¢lÂ©#);.îÇßÌ’(;~É&Nã­´›Hòˆœç™7r¤<þŸgÏÙ~P\¢•ãR	3dnU
Æþ‡VŸ,îaG÷°º{ö[3Ùçç¿?û†æû:ÌøÙ3þ\¬–Ú¤—í(/œ8{ÀªÉ&ºä“\4wúh*¼3â¿•4¢Â<·t¦4z!­ÔJªéeçL*2^å®µ›	f…YÈDÔ`RÍ„‘Ž«$Ü4<coØLð´‘`0yaüy[ŠDf2k¦t&RÜÍ&pÅá˜ÌèVZÙL[‡7dÒ²Tf™0B9–]¬]ˆ@Þ7tõëÍo£¾ÁÉAò³VGáãT­„H™Ól‚Jl`YHã*ž³‚'3PúíàD6ÛÀT¡©ùæk–i½mÉØhíÆ%w³¡€Ð;ÆûR¨¿~ÇøM Ê:žç€ÜBrVòd§àh×RÀ-ÂJ%7€„**ë˜Ò®†]úx0À¹Fì-ÌÕSçå
®d…ËñŠ¬Ês„XÊ¢ƒa¬Í@‰!VW&–ˆÑ…èñ¥.ü'œØÆ“i%¬Óö¯M†h¤2åNØp‹JÉö?¶”*ÕK»få °†4ve(jª¬™9áT€ Ðm@ØŒe9ž]•m@Ö´äºJƒ†˜,iˆ<"Z˜ƒN :†[Mr— uö×ôºÛkZùGÍ65¿(H3CÆ-›+½Tµ£ò§®À9pq7¼(qš`+¯}øÚn•Ã‘ñŸžDÔ'žˆÖA'Äî=
ÒQƒ‘Ç»Ÿ82Õ3Áæ-¡Öôn<èc™öx¬[LH29­È)ˆø©á¡ˆütõóÒy­ÍÝ:Wz\!ŠÒùDwr.¨9s¬pŒ£…pÜÜT¾&ÁDk¸•ZOöê¬D†MÞ·”yÎ‚ÊaLV‚°âõöRBúÄ;Æä¯Lt¦—Rb:Ã˜Nâ	ú±,D;n¦"Îí%ò¤€lŽnù¿¦ÌÑVe	_¥“å¬º v±ÂÞ¼µ“‰2ÔÇ “M ÜÖÉ4©&ã‰“0C .ÌÆ-ÌY$@¹$›p+}B¹.	HìØBãr:NÊê3Ë%ïÀÚT ½RÛ
NŒ }Ë)ºŠßpÌ¿wz™Ë²)µaÄÊ[ª!í”ò•¯ÅýÝ 8Tïˆ}ÿgÕÄÅCs	¸¨\Z*@WPÄ\²óáÅð8?{…¸œ N<°ÌB=˜ÇÔ«ÍŠBÚ[s£Ÿ¢ãóÁEÜö‚SnÜC¼â[‚TN@þÕÿ_Â(‘×ƒE>8òÊŠtÈ–3™Ìàg¨pøâüÕW¾¢ƒ~§:á 9›è
Xc%T“$†ªxH::ks©‚ ã‡{@¬ ³àê¥‹—(è©@*:yïåG”×@DÀb"AÑHæ¢V	Fˆ†õë¬¥_ÛÒ0SÃ‹¥€}__ìcß+íxO¾Cýb¸ö TÐÓtëàP+Ô.È×½2c_Ä9ˆmÝ
«^›â«óÊÕ‹T ‰Ö0Õ¦ÀÛVÁFìpDÂ'ÂC$ÑRU7,¡oØ!;» ¯Ç!å©T.éðÞ”3ÅÒ4°^.6Æâ¿ˆãPU1úÁô™mhjcDâÃ½IŽØ×ç_ü®	²à7Jáà êÐú±à5¯:WDÒÜRûkcÎ‘‘ò^úîˆ:ÖºãŠ¢ÑyOþä?Ðó^±,çSöâ£àÅKt‚Ü%jÁâ¡TZÿ3­üV11vu Jkägp=|¥œÛËIAúK(·¹[/Ÿ;GY¿C>€k.¼ŽD…ðN.î¼¢P^ûPüY…H%L;IÙÄçïßÿ	òƒôÎ„ßÑ‚ëÁÁ×ê¸ê ²U˜„@5&0n)ø‘Ùjâ/¿ÁY-Aô€hØ
v1jó‰˜µÀô‹='ãîÙ}WvûåÇˆÚo)èÛC¢þF^ùBª÷É
Šé—D•;{Ÿul Æd6r
Öå—6nÅšóÃô¡ä)sƒÐö!ÿœ>{Øa—¼,¥a=EîJ‘Û îþrÖj5Lôea;Æ-úÆköÕ.Ô½Q?	£þBvlË&IíCö…«Ì$ÂY?¼Øþkb-b€UÎ^—0ÉçR—BNg=½Ä!ü²à9{÷¦˜sÊ¥½vGìWó€tK!àC)ï 4
lÄŠf²t>à£E9ï6dRŽŸàZöùV8?aû´>6›û´,í0iÔµúÀ±7+³GÊüÄDdè!2H&qzÂ“ylË¾ú+`®³Ù×½Zäk¶4Ç§§”¡D}ñÛï/‰›ÅüNÍá×á2ÒØˆ]™‰t†#tð{½âŽk?ˆ‡w‡‰0„Î:~°[í¨@ZØJhäJ•Ã	æÔ€yÇÌDÌøB‚¸N1ŽÛþþÆZB¹Ì” ×ðåSd’¿".‡ê.$Ú]ÀïTv€éERG1:„3U_r¢öuƒÌ¶ív¨@Äéyˆ‡ñù1› ðNýKcì¸šÑ~ÏkÊo©Ó»7ƒñŸœfŸæ>â¤gKU+Lº)8lÊ­ .\²¨d"áßÐø×BQÜLª¹…B›Ù™®ò”<EI¸Ã 4öÐQ<·H‰µkß³*úÏŒú^‘ë¸.`µDtë!“' œvV9¤N¼»óÎÙÚ_JpÂù’¯,Æ_t}ôúzZzûþíW¿üøqHÔXÎtÞmô÷¤R?ÆQ<Þêb¦6&úÄB¥±(§Ëàÿ-U VÔ¿vªŒp•ÙV	Ö”0ÎË¤H/¯m•ÆÍ.’bjØ™eßÂØtò¦Õµß
fÙå|yyf8ªÏÃâ]‡66¶Ó¢¨%ý”(xá:r;öö‘œv ~ÕìºÕŠ?°	³_ä­ÆöX›.QYüôÖtÉ(h­€Û¹§íJ%aOài:äk˜ˆ	Õ¦~“Ô·xàzoèŠ þ‚u®[n§öÅ*·*÷ØðMª&‡Ë¸Ì©†ß—k¾jt\½ýÅŠ¬ÊIÑ-¥s9ÞUEÉ2™¿“6k38ßâHd< ¶mY ˜NV'GÔ§ÉÅ+ïÁ½Oã÷â…¯pß»­Ð¯PÍ—r‘œÉ5oDôäÎjWMmÆAFë›ÀÑwRßxÞqéþ+Ôí²F(X|_íÔä ÑÒu«R&@1(^âþ Z4Â]C`Ù ÛÏÉ®”Vg¡/üä4éUPhðÐpIX6ŠäºRÎ—Eè{Ñ82lœÀïU
 ËW8²cËhÄ–Ü5.Ù÷P»¸õ)ú[·æ|†mÃ˜jýìëjªzrS{RöòÁwä‘¦ýå¹,»y/àÖL’[¥bêO“¬ü'P¤£j²“˜5@êlª„VÁB×>Æô¼]G[P??8jÕ‘Ód	s!JÜÔåŠêþxõ–;_ö¶ãöz§º3êYõ8¬Š´¾Éžˆ÷a@|Ýð¾éäàšQ8àOß} §Rï!Ý›q¬ÔLlÁ‚zŠ}7:°‹“»5–õ§‰HCŸã-¦>ÊÜá8>\ý"R½©7ëk…ÝYWy~›´°M€¬ôQ½ÿ¼@P=}ˆ]”Šn´ƒ¥Ñ“1ûÂŸP	?½§‘N‚¥~õÀ÷{7†^+Ÿ¥aÇ…¸Y¯Õ±áD¦ØÒ´fJïø³‹+µ•*«4ïP5ôœÐ,×+¿O;¸æH_ÓýögNb|Ê‘îX”\5`†ŸéûˆÄH°Ý-lïtÜ×.o5boÑ¦yŽ ÷˜Œ©J¹°tðØ½„\jëzôîŽÏüöøƒƒ·†Çô êº0¼õŒ1^Ê½ƒÙÝÅèzÜîor[‹ýævšÝOÈÔÂ¶Ä}Ì¬îoØgb=V÷3¯ûBÕ`pˆYéÓ+?ž”U5{ŸlTzÏkž=P÷2©{áT#°¨	6‚VÆ}iö¸¥ÙVà¶!r(Ž}•ö¨UÚ8Þ©NëÀß£ø8ÛaÆøI¦ØCø(µÛfø)FØ—Ç-ã3¾Šƒ.Ô=lG­è4¸;ÁÖwG.î5µÃ«†¾Î;rw°íÃ,Ê@ÇAƒ§÷`Ïq[Î¤¶ä‰8³ß]íhõã<ø*	é{  ÇBø[ž$01•RÝÇé9Dš4åÂU+ˆÔ¿)ñ	ËõÍøoLß¼ø–ú™ßG£QxÏþî»Zu!½%cP•Cë°]­*õ{eºÒè9ð ¸¡H\Ý+m×[SJ ‰öö½õü9*N„=wâå=oŽÊÒñAœÙÂ”HÜ]8s+S6dÕ¶vòE—=]ŽL]>u¶D$Ø˜°½|l–Á”öÏ¸/mÏ‹RÃéAï„èŸH½þmýYfžžùùGŒ«ãG@m_Û¾4Õ¿ŽµÇ÷1ñå	) œþ1–³¯Ù‚1ÂVÜ$yeåBA[SpE†+GÏó½$ÔþŽ/Z³pœÞQ½QW‡·RE²èo`©Do«òý°3‘‡w`E‘øfeéÿB$rý¨áEµíƒ%T0uŸè:ïô
¬Ó¶äÔÜ‰ÊajN<P[»ýk8k0°˜’#z8~"p`¨(‡ûÀdèx$n¢a¢þ%Ÿ1·½º·eþ`óïCÜòúÞ[ÞÙÛŠ8à¹–Þ~nôÿúà…„×j     0707010005c806000081b40000000000000000000000015ab2175400000eb1000000660000000000000000000000000000004500000000root/usr/share/doc/opensvc/template.service.container.docker.conf.gz  ‹      í]moÛ¶þÞ_A ÜâÚnÒ®Ø–­ÅÍÝ:`ØK/n÷Ã08´DÙ„%Q#);î¿ç’åÄ±Ó¼,î•u‰%‘çyÎy¤<}z—Ÿ'OÙ~P\¢JËe)ôÙu%Xª’…Ð-înGw·º{ò{3Ù§G<ùšæûÆÏøÉþB¬WJ§'í(t]NU¼Láøè?¨-“¨ŠOsÑÜðƒ®ÐâÏZjÑŽä{ž:Riµ”FªR–³“Î‘Td¼Îm;ø_TId9ZZ^&þF¹à{Ëæ‚§a‚Á¼…vÇM%™ÉÎ™	P—LH;p“Àæ‚y0«˜¸ImÜ†ÙyÃ F×L•¨Å1ˆø:Ò&hþ…ªì«Š„ÛSY¾€ÿó\–|læÛà(y!ÔŠz)&pç˜œ	ËdF·j5?WÆâ™4,•Y&´(-Ë´*6NÄ“ :ûÍåo#Üàà ¹?Ó U—BaLQ‰,K©mÍsVðdJ¿œÈ…60…š+ß°L©mØ(³L&Z);©¸÷y„~ph¼¯DyöÛ·ŒÏÐ$ Yð>9 ·”œU<YÀ¡àÓâ(	G(U\l¨¨e¥²v¥éÀƒÎe0fï`®Ž
<¯A(/áÜH–?ÏÈê<GØˆ% ,úÒµ(1Ä¨Z'Â1ºú)áÀuŽë™éâòîÂjÎ@%uú6IÅ	ÐøøjApÈr¹ TUW0—)4üPÚ‚WÌÇ41õðŒ–!'rìƒØ ìøÄý«WlT±W_Œ_5þâxüòõÉ—G_Ñ?Û0u'Þ…²Í‘;–¡¥¥2åV˜“v´ì¿í}À¾—BG#˜*·*7=‡·P¼|H@oÅQnñÊ²Õ\&sÖõÊ	ê±ád±:9·ªR¹š­ÏÑù;e¹¸`”gµÌ3+®‹X˜%è|Ú’VBõ—H‰i] ßÜ|¯ñ² ó·Ò%x~àC«rÒ«À)	}œA­¤7æíNÃ¾|•½ä¯ÒôåWÛMuVcÕÿ¹÷½ÂDëR^€‰®d™ª•¹RžSd4kcE"õF–Ex (tki¡&•¢¨ì„OÁk"Ýk,\?6©×Jæ9ó*‡1	j@_°BÉˆÅxÇÍ¹3CýˆyLˆ©|n8ˆè?DpÌÎdm!äŠ8½–E•ðt;HÁ%o¦®(d“cÏ9¤t…J{&ÌÅóq;™HÐ C}0ß#ÂÙ`“LŸí1žX	3„ìæãzÉP:Ç¦ÜH—.lJÚåÆ«Ù$©êOÌŸÜ€µ© z¥¦œhúžT3t¿ã˜ÿèô4ÏÕª©vaÄMS²7×(A`¥rØÝ€CõŽÙ·ð/$‰ü½9ò€\ª4/!œ°£áñð%~q4z‰¸zœ Nüb;˜…(z0ïLP¯Òk°Ü”j¨æF?Gß#Ì7qÛ	.0`bæ ¾‡øzˆ¯6²´b&tþ….EëëlpäµiHÄkùfË†½üœ9ýƒo/;á MU¬12
CõY­Egy,U@tüpˆt\½´q¾O÷ÀHRÑÉ;/?¦L"&,	ˆFŠ¯Œ9Â‡#ÁÝò’‚a4e†Jû^ïbßŸµ²¼'ß¾þóH1Ì?‚
ƒz"òŠðI@DX-.h¦U]a¹÷Yœƒ˜Ö­ ¹ÂòŸ•ãŠºã/H¢eÄò²À«¢Æì{pDâ‚c‚3Dý$Ëú‚%t…²Ñ1x=)O]æ²ïM9S·@…Zryi,îBGYS\ÜÏX!­Œ MM¬‚Hœ€lõ&U:f¯>û'œãeÁo”ÂÁ¨CãÆ‚ç¼ìœI³+åÎ9ODFÊ;é×GÔ‰RÅ‹7­òžü×’OÏ{Ê²œÏØ³#Œ‚ÇÏÑ	Zp—¨ƒ_¥Ò¸Ÿiñµ¶ˆ¹±¨PZ#?ƒó¹ç+åÜNN
ÒŸ!“7ãäsk)ë·ÈpÍ…óÀ‘(ÞÉ%Ã×ÊƒÅŸeQˆTÂ´s?”M]ŽðþýÏ‘ 7Hç±áˆÿ-8.qÔ'@ek?€jL`\Õw#3õÔ°_ã,¼– z@´l;·ùDÌZ`úñŽ“q÷ì¾)»üKt×ƒ)è›}¢þ¥¼ò™,#Ü§k(¦Ÿ{Õvô>ul ÆdÖrÖ%°Â…ªÑˆç‡é®Gƒ*cnÚ.äÑg;ÌŠ¥AXO‘›Rä*ˆ°œµZõ}Ù¥°ã]ñ†}~jËÞ¨…Qÿ!»oË&Ií}ö…[ŒS$ÂYk7¼Øþ±–±GÀ*g§K˜æ©&+!góž^wâþYð‚ýð¦˜sÊ¥vÇì7ó€´+!JÀ‡RÞc hì+ØˆÍdé¸Ç7F‹rÞ­›:ÂX~€kÙG[áüˆuì}ÐúÐì¯Ó²´Å¤QõcoVf!”ø‰©ÈÐCdLâ.Ä”'‹Ø–]õWÀ\-f³	®¸ý!/òÛ
šå¸%ê³ßÿx~Ÿ¸\ÀïÔn.#Ù©žJ«9B¿‡w\ûA<œ;tHø!tÖñÂÆo=´£i~+¡‘+Ël&è6f5˜w\ÁLÅœ/%ˆëã¸-àî?`|©$”Ë¼„ÀâzA¾|†LrgÄåPhr·¡Ý¼¦6L/’Åè+œiù‚Õh´od¶°+qxân|~Ì&¼3·ÆÒ;®f´×9M‘jÝ×7`yã?8Í>Î}ÄSHÏVeP*˜t’wØ”[\¸dPÉD¦MEEøØ¹,
mfæªÎSòUÓjÑGc÷M]ÞsûæŠàÚwlÇùŠþ#Áß¾Sä&î€X-ÝºÏä	(§™×©ïîü`Mð—œp¾âk‚qçŸƒ=?û®Ú’}÷îûÓ_ú0$j¬æ*ïví¹{R§ã8ï¥êb®Jìt‰E§YÌXUyÿo¨0"üÚY¨ÒÂÖf['XSÂ8O’"=97u4³LŠ™f#Ã¾±aômÕ¹Û
f\ï‘Ÿá8‡Ä»mll§EQKº)QðÂuäv íí#9í@ÜªÙy«÷Åe˜Ý"oXÛamªBeñÃ[Ó%£ µnŽ¶ë2ñ{Ó!ÿ;ô°Qmê6I]‹®÷ú®à‘ç/>°a¹Ú«ÜºÚaÃC6­›.ã2§þ²¸\A0p XP£sXâêí¯FduNŠn)MÍ›i]T,“¹p«±1i³6ƒs]¶žd@Æ=`Û–uŠét}pD}œ\<uÜùÔ8~q!¾¤ÐàîÚ§Kô+®ÁˆäÝîL¸ˆèÈWM­f^Fë›ÀÑwR×ûÝqéîêöñY#,®¯€ö9Hc´ôBíº’	PŠ—¸?ˆp×X6Àp²«R•#ÚòMÑ}.@“N…§øe£HN¡êÒº²}/G†x]]¦ØMŠ#Ë0¶ŒÇlÅ5Qã„}µ‹Ýœ¢»ukP~Î'~xñÐ.SÐÏ®®¦ÀÐƒ3˜ ìQÙË™ï.æ´å±z!«nÞ¸5“$ÇV—1áÓ$+ÿñé¨†šì$f:ë:¡U0ß¹‰1=o×ÑÔÓ	ŽºìÈi²„…îªjMu¼zË­+{Ûq;½SÝõ¬zVEZ¿Ìžˆ·a@|ë9à|ÓÁÁÿ8£ðYûtÄ]8•P¼ûtoŽWä&¶†`A=Å¾ØE‹ÉÝYÀ2|šˆÐ™Aú(s‹ã8;ýÅG¤°©ÒŒ„JIìÎ:Íó«¤ùmd¥‹êÝøç‚âè@ìŠ TôR;˜[Fðï¢LxÕ³ô>XêV\¿w¡ïµrYv\ˆ‹ÍZNdŠ- Mk¡Ô¹Æå˜­X\©­P¹LóU}Ï	í@Árµvû„±ƒk7qE1Ýowæ$&‡éîëéžÓæAÌð-]Qx˜ÝoïtÜ×uÞjÌÞi­tóî1i]W6ra>;éàqýr¥ŒíÑ»9z<sÛãwÞ;Ð¨{èîÀð63Æx)÷fw£ëq»½ÉÝl,v›Ûav;<"SóÛ·1³Ðß°ËÄz¬ng^·…ªÁ`³R‡W~<*«j6ö>Ú¨Tµ—Mõ@ÝÊ¤n…S@àz ¦ØZ™ô¥ÙÃ–f[Û†È¾8öUÚƒVi{àx£:­âÃlûãG™báƒÔn{šáÇa_Üo·ŸñíQt¡îa»×ŠnOƒ»l}qwÏÅÝ¾¦¶ÕÐ×y÷\çímg»0‹2Ð‰×àá=Øs?ˆ­æÒ
SñDŒŒÀwW`;Zxœ_%!] àX·cË“D &º.Ëîãôœ"Mšrá´Dê_Tø„åæfü7Z¦oŸ}CýÌo‡ãñØ¿çw]­ª–Þ’1¨«¡±Ø®VWƒð^™®4zNÇ¿ n(z¥ÍfkJC	 ÑÎ¾·ž?÷ÊŸaÏ¸Cy@Ï›{åéx/ÎlaJ$î&œ¹’)—d…Ûˆ×òEU=]î™.ªzìl‰Hp}`Âöò‰^=SØ?á¾´/JõO¤{½¢S| ôú·Íg™y:r/r>hâ‡GmWÛ¾4Õ½ŽµÇ÷!ñå	) œþyšÑköŒ`Œ°I^¹Ä7‹¸"ÍKKÏó='Ô^á‹Ö|Oï¨‹Þ¨«ü[©"Yôg°T¢·U¹~Ø¹Èý;°¢ŽÈ1{w!Ý_"ˆDn¾ Õ¿¨¶}°„
¦î]#ÿN/ÿÈ:}ÁVœš»#Q9LÍÊ‚{ªck·{§uoSrDÇOeña˜}‰›*˜¨{ÉgÄU¯îm™?¸ü'®x}ïïìmEìñ\KïF?	7ú?ÔlúYék     0707010005c807000081b40000000000000000000000015ab2175400000de7000000660000000000000000000000000000004200000000root/usr/share/doc/opensvc/template.service.container.esx.conf.gz ‹      í][Û6~Ï¯ N°¶;“m^¦M°³m
½d¤Ý‡¢ðÐe–H•¤ìñbüžsHI”={2ãÙq*If,éðò}çF1ÏŸ?äçÙsö€—hå¸TÂ™[—‚	{}qÛ»‡»g¿7ƒ}~öÇ³¯i¼opÄÏžaßb½Ò&½h» x!àÂè?8G6Ñ%Ÿæ¢ié£©°fÄŸ•4¢íÂ÷<·t¥4z)­ÔJªÙEçJ*2^å®íµ›f…YÊDÔ`RÍ…‘Ž«$4šž±·l.xZ÷H0¼0þº-E"3™À=3&š"hÍ&pÇá˜Ì¨©fNÙ\[‡2iY*³L¡ËŒ.6nÄ›Æ ïkºûÍvwÀQ7prü¢ÕQ`ø8‡©VB¤Ìi6ÅIl`YJã*ž³‚'s˜ô›Á‰¶©~ ©yòË´Þ…¶Ëdb´v“’»yP@èÆûR¨¿}ËøU Ê:žç€ÜRrVòd—àÛ®¦€M„#”Jn 	:TTÖ1¥]»6ôð`€cŒÙ;«§Ï+ÊÜÉ
·ãY•ç±&‹¾}m:J±º2‰°DŒ.ä@/uéàPpaOf•°NÛ¿6A¢‘Ê”;aC•’×ì¿l%UªWvCËA`)'hìÚ:QÔTÙPsÂ%L1 ‚Bwa/…fJ·`¨ñÙB¡¾ðˆv4…›hN‹k÷e’Cc¤Œè”²Ö%V­žFZ•J»`™ÌƒŽ ¸Û'Þ¡³‰‰xLÎ€ç;10‚ÜX"EBg·c¡ô¤4B¥›ð©6îä¬TsåXf
{fªi&~)LÔƒ©ÖÐ”Út‚µorðü`ØøÃ•Dhý”CŸ¬„iÀHLVp0ç±ûw&º( gäœ`@påÍE¼@Á1û !t!Ñ^Ã#q²(sQ€—£æ .ÒäQmU–ð(ù^–sð³…N{‰ÂËq;˜HÐ Ãù &ÂÙ`“Lƒà‚Oœ„‚K…1B¿…E²`ÈÇ²)·ÒSwSØ°ƒÆål’”ÕgæcïÀÚT ½RÛ
NŒ€ùž”3t›¿cŸÿèô2ÏõªIA ÇâXŠ©ÁJùÚç(¾5 §wÌ¾…¿7 ª¹…w¤¥ÀÌpÁÝ;ž_ág£WˆkÀ	àÄ/vƒYˆ¢óx`Âôj³ÍMÉU4ý}0ß\Äm/¸À€‰ƒøâÛ!¾ÁÙHåÄ~ÿÂ(‘×ÉòÊŠtÈVs™Ìágˆ™ðõùÙ«¯˜Ÿ°íªãn šÑTWÀ+!ÊF'1dÝTFtÖ,RAÃm€¯ «`ê¥‹S7j=HE#ï­ü˜"ðÄƒ'HÀAÈRO	zˆ†õ‡#Á}Î¯¡¦î‡wJû^ŸïcßŸ•v¼'ß¡öH1ÌÉ4‚Š¹BBÈkÂ'uv]› ™ÑU‰ë3_Ä1ˆmÍ
«ÎÙùÔê¼ruò’hmGm¼iu`Ì¾C$®98C$ÑORU×,¡'ìÎÁêqy*•ËB:l›b¦XšÖËåV_üƒ”UÅèÃ/db´  ©§ ' CÅy“:³×g_üî	²à7
áàœCëû‚÷¼êÜIs+íï9ODFÊ{é·{Ô‰ÖÅó£óžü·’ÿ@Ë{É²œÏØ‹3ô‚ç/Ñ:0—8¿‚„ÌÿL+b•CÌƒ]@ÐIÞ2¸Ÿ¾RÌíå¤ ýå"y»°^>wŽ¢~‡| Ó\x‰
îL2´¼&W^ÛPüY…H%;IÙÔÇïßÿ	òô®„ßQƒëÎÁcµßé0eë0p€jL`\jõ=³ÕÔ/ê|£³Þ¼`+Øù¸'bÖÓÏ÷pœ”»g÷]ÙmåD‡ÚïÈéÛC¼þV\ùBª÷é’é—D•½ÏF`¸åŒœv	Ìp!k´bÃøaøPr‹”Š¹Ah{—FŸ=ì°+^–R°ž"w¥ÈMwÅ8kg58L´e[n;Æ-zâûê6Ô–½R?	¥þBvlÍö‹¡MË!úÂ}Ÿ© á¨ï^¬ÿ5±–±EÀ,g¯I˜æ©'+!góž^bþ	Qð‚ýð†˜sŠ¥ýìŽÙoæ éVB(À‡BÞs h2ØˆÍ`ézÀ7F‹bÞ]È¥?Áµì³p~Â:ö!h}l6=iYÚaÐ¨ëéÃÞ¬ÌB)°S‘¡…È ˜Ä-‰)O±.ûì¯€±:Œf\? ôj‘oØNÐŸžS†õÅï¼<&nð;9‡_‡ËhÆÆìÒL¥3¡ƒßëw\ûA<¼9ôH„.tÖñk„mØzh{ÒÂVB#WªtwŸæÔ€zÇÌTÌùR‚¸N2ŽÛ¾ýãK-!]æ
R ‡ë	Øò2Éß§Cuu†o†vð™Ê0¼Hj/F_áHÕ—œ¨F½}Ó ³k2qzâal~Ì&p¼3öƒ²ãjFûœŸ)¿¡H?Þ¾¡”ÿäföiî#^Bx¶Rõ¤‚J7 ƒM±À…Kö •L$ü
¢Z(ê›Kµ°h3;×Už’¥(Éw€Ê*m‚å)1 6í{¶ãBFÿ™‘àÿ¾ŸÈMÜÐZ"šõÉPN;¯R'ÞÝùÁÙÚ^J0ÂùŠ¯-Æ_tuôêjJûîÝ÷—¿þôqHÔXÍuÞ-¥òmRmïã8îïVv1×
¶|`¡ÒX”Óe°ÿ–2 +ê_;UF¸ÊÀh«sJèçER¤W¶Jc‡f—I13ldÙ7ð#Vñ½­{uå·ÂÀ„Yv±X]\…Žëë0‚x×¡õí°ÈkI?$r^¸ŽÜv¤m>’ÓvÄ¯š]µ³â¿Ø†Ù/òÖc{´M—8YüôÖtI)h­€Û…§íZ%aOàiäip˜ˆ	å¦~“Ô—xàzo¨Š þ‚vnjn'÷Å,·*÷èðM«&†Ë¸Ì)‡ß—kp¾j4K\½ýÕŠ¬Êi¢[Jçr¼«ŠÒÑjlLÚ¬à|éc ñ ØvE]€b:]ŸQŸ&/½÷65ößC\ˆWäü„ûšV…v…r¾”ðäLnX; ¢'wV›j*¿2ZÛ†¾*ø‚ÜŽI÷PµOˆ!añu´/P“ƒfŒ–~@¨[—2ŠAò×Ñ¢îËX–Kz¥´Ñ–oJuo0“~

n	ËF‘œBWÊù´m/*G†…ø\¥ ²|=ËÐ·ŒÇlÅQã‚}¹‹Û¢oºU¨0æ‹Ð½¸k[ÊTÏÏ¾ª¦š¡'§0µ°'¥/|EÙhÚ_^È²÷nÍ É°U*Æ þ4ÁÊ¿E:SCEv£M•Ð*X¨fFŸ€–·khªsC­:rš(a!D‰{ƒº\SÞ¯ÞrçÓÞ¶ßÞ@ïâTwD=«‡UÑ¬o³'"Ã}·q;¼m:9øŸ¦øÓ³`Têä=„{sŽ™š­Á™SO±î\VÑbp÷¡Æ²þ4ièc¼åÂG™;ìÇ‡Ë_‚Gª7UšžP*‰ÕY—y~“´°M€¬ô^½ëÿ¼@˜8z+«"(Ý*óËáá=îO¨äq^Øøë±Ô¯øzïÂPkå£4¬¸×›¹:œÈK@šÒ,B©óŒ1[±¸R[Y ²JóUCÍ	í@Ár½öû„±kÞtðI1µ·?r“SötÇzÙä²y‘ #üÄH_G$®E‚åna{§c¾n³VcöÎmš÷pÉ˜ªt‘	ÑIÛ—Km]ÞÝÑã™ßpð6ðØƒ@ÝC÷ Š·1ÆK¹wP»»(]ÛýUî!`ë`±_ÝN³Úá	©ZØ–¸šÕõûT¬Çê~êu_¨Q+}zéÇ“Òªfcï“•Jïy—<àÙu/•ºN5·5ÅÂ@˜•IŸš=nj¶¸]ˆŠcŸ¥=j–v ŽwÊÓ:ð÷(>NÂv˜2~’*ö>Jîv ~Šö©ÁqÓ¸Ã”ï€ä uÛQ3ºîN°õÉÝ‘“»CUíð¬¡ÏóŽœç¬gû0‹"ÐI˜ÁÓ{±ç8ˆ­æÒ	[òDŒ¬À³+°­~’¾ p,„ß±åI" S)Õ}ž³A4Óƒ&]¸lÑ‹ú×%¾a¹¹ÿ‘éÛßP=óÛáx<ç|áï¾ªUÒÑ)ƒªZ‡åjU9¨Ï•éJ£÷tÂ1 Ð H\]+m7KSJ ‰öÖ½õü9*N„=wâÅ=oŽÊšãƒ8³ƒ)‘¸»pæF¦lÉª+l#ÜÊ]öt92]tùÔÙ‘àvÇ„åå³z¦ì>½öó©KÛsPjx#=Ì;!:Å·RAÇ¿m¾ËÌÓ‘?@È¿ú`\í?jûÊÆðÐTkïcâËš 0ø?TŒ^³c„­¸NòÊÊ¥ÂlÍÀ®½Ï÷’Pû;´fá{:£.:QW‡S©"Yt°2¦JtZ•¯‡‹<œUDŽÙ»ké‡Dn€ªm_,¡„©ûF×(œé^Y§/ØŠSqw$*‡¡9Yð@u,íöÇp:j0°˜‚#z9~*°c8Q_öÁÐ÷‘¸©†úC>c n:º·eþ`ûÜüŽï½áÌÞVÄïµôfô³0£ÿR@ìg   0707010005c808000081b40000000000000000000000015ab2175400000d86000000660000000000000000000000000000004300000000root/usr/share/doc/opensvc/template.service.container.hpvm.conf.gz    ‹      í\ÿoÛ6ÿ½bp‹³½¤·ý’µÅå¶n[h·û¡Z¢lÂ©‘”î¿÷)‰²ãØiâ\ÜS€­‰-=‘ïóyßÈ'>þ?Ïž³üAq‰VŽK%Ì¹u)Ø¼\÷÷°£{XÝ=ûÔLöùÙÏ¾£ù¾¡?{†ƒ_ˆõJ›ô¢ƒâ…€/FøƒJ²‰.ù4Í“>š
ŸÃŒø³’F´Cø‘ç–¾)^J+µ’jvÑù&¯r×ŽÚÍ³Â,e"ê	0©æÂHÇUšž±·l.xZH0˜¼0þ{[ŠDf2kf4&R<Í&pÅá˜ÌèQRÙ\[‡dÒ²Tf™0B9–]l\ˆAÞwtõ›íï€£~ÀÉAò«VGáãT­„H™ÓlŠJl`YJã*ž³‚'sPúÍàDÛÀTßÐ€ÔÜù†eZïÂFÛe21Z»IÉÝ¼G( ô“Gã})Ô‡ß¿g|†&HeÏs@n)9+y²€¯àÓ®¥€S„#”Jn 	6TTÖ1¥]»6ôð`€sŒÙ;˜«§Ï+Ê\É
—ãY•ç±”E†±6%†X]™DX"Fr Ç×ºtðŸPðÅ.žÌ*a¶ÿßaˆF*Sî„¨”¼fÿa+©R½²VKa8Ac×Ö‰¢¦Ê†™.AÅ 
ÝÛô¤4B¥›ð©6îäi¾9$8Z€¤y˜åR˜hS­áQjÓàk;tpÿ`ØØþJæ9*‡1Y	jÀ¨°jß‚	ñØÔÃ•‰.
"LˆéŒñæKü‚þCÇìƒ7M¢73ûwY”¹(À¢éq4y[•%ÜJ~†å|J¡SÁ^{ýrÜN&4ÈPt8$@¸!l’iÜã‰“0Cp0G·0£H(€ü	›r+½SÙ”,ö ì q9›$eõ…ù“;°6@¯Ô¶‚#@ß“r†.âŽùA/ó\¯štFîÜRþ ®PÊ×>óOàP½cö=üŸqÓ	 Í%¸#-!Ã²v6<¾ÂÎF¯×€À‰ì³EæñÀõj³ËM)ˆ7ú%úa¾3¸ˆÛ^p;ñ=Ä·C|C°‘Ê‰™0øÿ!Œy=Øè#¯¬H‡l5—É~‡ü x ŸŸ½ú†yýƒoWpÐŒ¦ºÖX	‰!ƒL®2¢SŸ¥‚Žž±‚¾W/]œ¦Ò30RTtòÞË)“ˆ€	D‚¢‘ÌE­Œ9êŽ÷õ†a˜z>Xt(ìûö|ûþ¬´ã=ùõ/X@Šaþ©TÐ‘×„O"êJ¢vA3£«kÑ¯âÄ¶nÈU×'|ju^¹ºPITÇªm7UBcö#8"qÍ1Á"‰~–ªºf	Ýa‡lt^CÊS©\Òá³)gŠ¥i`½\nÅßˆãPU1úÁô™mhjcDâdã¨7©Ó1ûöì«¿Á5AüE)|€:´~,xÍ«Î‘4·ÒþÚ˜óDd¤¼—~{Dh]L°ª4:ïÉ+ùô¼—,ËùŒ½8Ã(xþ w‰Z°øQ*­ÿªÿÊ!æ!Æ. @iü®ç¯”s{9)H9„LÞ.¬—Ï£¬ß!À5ÞG¢Bx'—O^S(¯}(þ.‹B¤¦‡¤lês„÷ï‰ùAzß„¿Ñ‚ëÁÁmuÜõ	PÙ:ÌB —•üÈl5õìw8‹ %ˆ­ [ÁÎÇm>³˜~¾‡ãdÜ=»ïÊn+ÿ-:Ô~GAßõ·òÊRE¸O×PL¿$ªÜè}6êØ ŒÈlä¬K`…U£ÎÓ‡’[¤TÌBÛ‡ü3úÙÃ»âe)ë)rWŠÜä q€³V«!`¢/Û
Û1nÑoØ7·¡¶ìúIõ_²c[6IjŸ²/\ãž
 ÎÚøáÅö_k{¬röº„i¾z²r6ïéõ áï/ØOïaŠ9§\ÚkwÌ~G0/ H·B>”òž@ãPÁF¬h&Kß|c´(çÝ…,PÊñ\Ë>Û	çg¬c‚ÖÇfƒ‡–¥&ºV8öfeòHY€Ÿ˜Š=DÉ$îBLy²ˆmÙWÌÕa6›àú¡W‹|Ãv‚æøìô‚2”¨/>ýñò˜¸Y\ÀïÔ~.#Ù¥™Jg8B×+î¸öƒxxwè‘Cè¬ã×Û°õÐŽ
¤…­„F®TØ0L`N˜w\ÁLÅœ/%ˆëã¸-àŸ?`|©%”Ë\A	àp½ _>C&ù+âr¨Þ‰ö¡Ý¼§²L/’:ŠÑG8Sõ5'ªÑhß4Èì`V âô<ÄÃøü˜Mxg~¥1v\Íhïóš"Õú_oï Ærš}šûˆ—ž­T­T0é¤à°)·¸pÉ ’‰„CóGEýãæR-,ÚÌÎu•§ä)J
Ä ±‡®‚à¹EJ¨]ûží¸PÑa$ø_ ï¹‰;àVK„@·2y*Àiç•CêÄ»;?9[ûK	N8_ñµÁ¸ó‚®®Àƒ^]C[—b?¼ûñò·Ÿ?‰«¹Î»m#þ™ÔGäÇ8ŽÇ»U]ÌµÂæŸX¨4åtü¿¥
ÀŠúÏÎB•®20Û*ÁšÆy‘éÅ•­Ò8 ÙeRÌYö~ÅŽ¥·õ¨®üV¸0Ë.«‹«0Ãqý=Ì Þuhcc;-ŠZÒO‰‚®#·iÉiâWÍ®Z­ø¶aö‹¼õÂØkÓ%*‹ŸÞš.­p»ð´]«$ì	<M‡üO1¡ÚÔo’ú\ï]À£À_°ÎMËíÔ¾XåVå²iÕäp—9ÕðÛârÁÀw‚`AÎa‰«·¿Y‘U9)º¥t.À»ª(Y&sáWccÒfmçÛ¼É€ŒÀ¶+ëÓéúäˆú4¹xé=¸÷©qüâB¼¢Ðàîû÷úªùR. ’3¹áí€ˆžÜYíª©Õ,Èh}8úNªà›;.ÝßBÝ>!k„‚Å÷Ð¾@MÒ-ý€P·.eƒâ%î¢E#Ü5–°‘ìJi5¢-ßÝç4éUPhðÐpIX6ŠäºRÎ—Eè{Ñ82lœÀû*åkY†±e<f+nˆì¨]Üæý£[ƒ
s¾Ã‹‡¶eLµ~öu5Õ=9ƒ©…=){ùà;òÈGÓþòB–Ý¼pk&IŽ­R1õO“¬ü+P¤£j²“˜5@êlª„VÁBç&Æô¼]G[PO'8jÕ‘Ód	!JÜÔåšêþxõ–;_ö¶ãöz§º3êYõ8¬Š´¾Ížˆ÷a@üŒÛ9à}ÓÉÁÿ4£pÀŸî} §Rï!Ý›s¬ÔLlÁ‚zŠ}7:°‹“»5–õO‘†>Ç[Î }”¹Ãq|¸ü5D¤zS¥	•’Øu™ç7IÛÈJÕ»ñÏÅÑ(ØA©èV;˜_F7ï	B%¼ìYz–úÕßïÝ@z­|–†âz³VÇ†™bHÓšE(uîñ9f+Wj+TViÞ¡jè9¡"X®×~Ÿ0vpÍ{0¾(¦çíÏœÄä”#Ý±^Dºl^$À?1Ò÷‰k‘`»[ØÞé¸¯Û¼Õ˜½3F›æ=Üc2¦*]äÂBvÒÁãö%äR[×£wwôxæ·Ç¼<ö P÷Ð=€ámfŒñRîÌî.F×ãv“{Ø:Xì7·ÓìvxB¦¶%îcfuÃ>ë±ºŸyÝªƒCÌJŸ^ùñ¤¬ªÙØûl£ÒåA6Õu/“ºN5·5ÅÆ@ÐÊ¤/Í·4Û	Ü.DÅ±¯ÒµJ; Ç;Õiø{§`;Ì?Ë{¥v;Ð?ÇûÒà¸eÜaÆw@qÐ…º‡í¨Ýw'ØúâîÈÅÝ¡¦vxÕÐ×yG®ó¶³}˜Eè$hðô^ì9b«¹tÂ–<#+ðì
lG«_çÁ£$¤ï áwly’ÀÄTJu_§çlizÐ”—­ zQÿºÄ7,77ã_™¾}ñšú™ßÇãq8çÿö]­ºŽNÉTåÐ:lW«ÊA}®LW½§Ž€ŠÄÕ½Òv³5¥¡hoß[ÏŸ£òçDØs'îPÐóæ¨¼!Ä™L‰ÄÝ…372eKVÝaÑàV¾è²§Ë‘é¢Ë§Î–ˆ·&l/Ÿ˜Õc0¥ýîKÛsPjx#=èâÛ © ãß6ßeæéÈ ä_}0®Žµ}mcxhª?ŽµÇ÷1ñå	) œÇ?ú–½ #lÅu’WV.Å´5Wd¸rô>ßKBí¯xÐš…ÏéŒºèD]N¥ŠdÑ9ÕX*ÑiU¾v.òpVÔ9fï®¥?
;¹y j8¨¶}±„
¦î]£p¦Wxe>`+NÍÝ‘¨¦ædÁÕ±µÛÃéü©ÁÀbJŽèåø©À¡¢¾ì“¡Ï#qSõ‡|Æ@ÜttoËüÁöá7ß{Ã™½­ˆÞkéÝèáFÿýÚÁÙd    0707010005c809000081b40000000000000000000000015ab2175400000dea000000660000000000000000000000000000004300000000root/usr/share/doc/opensvc/template.service.container.jail.conf.gz    ‹      í]mÛ6þž_A (œàlw7×öÃ¶	n¯M /9 iïCQxi‰¶y–H•¤ìõá~üÍ)‰²×/Ûµ÷Ö9H²kË#ržgÞÈóüù1_Ïž³#¾P\¢•ãR	ÓgnUö/.³ˆ;îèŽ«»g¿Õ“}~ñû³¯i¾¯iÆÏžáàçbµÔ&½jÆ x.àƒÁ_¨$›è‚3Qßé£)ñ>Ìˆ?JiD3„ïyfé“Âè…´R+©¦W­OR1áeæšQ»™`V˜…LD5&ÕLé¸JÂM3Á'ì›	žV#&/ŒÿÜ"‘™À5S“	©îf¸âƒpLNèVµRÙL[‡7dÒ²TN&ÂåØÄè|íB¼hò¾¦«_oŽxÕÎ’Ÿµ:	g j%DÊœfcTbËBWòŒå<™Òï'²Ø¦ê5Hõ7_³‰ÖÛ°Ñv‘ŒŒÖnTp7ë
½óh¼/„úðë·ŒOÑ$ ©¬ãYÈ-$gOæð¼Û¶pÊ€p„RÁ áÀ†òÒ:¦´«`×†þôp.½!{sõTàY	B¹‚k#Yár¼bRfÂF,eÑ›a¬õ@‰!V—&–ˆÑ†èñ¹.ü
>ØÆ“i)¬Óöÿ› ÑHeÊ°á¥’·ì?l)Uª—vÍÊA`!'hìÊ:‘WTY3sÂ%¨ A¡Û€E‚Va"ù™´nCõ²X|Áxša­°L{‹Æ´´zÜíeñU§éC5ýÕNMµGÕx):¤ÇPw­»}W@<ðyš%%¢VØC¿‰ÓfUi·Ž5¨áJ»µ¬ô¨0Bä…ñ±6¢í£’»þäTÎGÎ¼¾ÏýÇZÃ­ÔzªPEpßïõë¬a)³Œ•Ã˜¬5`¾A+ø‚/“„pe¢óFF!&„ˆóúCü€þ ‚CöAC‚G¢7Sg†2/2‘C.@·
iÊ;lYðUÊPXÆ!Éu*Øao_›ÉD‚zÔGS \ŸõÖÉÔ‰
ã‰“0CH<`Ž0na‘,P e"lÌ­ôéÈº$ ±`‹é()ÊOÌ5ßƒµ© z¥¶œúSL.~Ã1ÿÞ"èu–ée]¨Áˆ!´Ty@Ò(e+_Éù»p¨Þ!ûþfÜ´RÏún) 'W_±‹þeÿ¾q1x…¸œ N|c;˜¹È;0O&¨Ã†#G{«oôSô>Â|op·½àFvâ;ˆwC|G°‘Ê‰)øþ„Q"«JDpä¥iŸ-g2™ÁÏPY àíË‹W_0¯ðíªn šÁX—À+¡Á ÑgP–F´VvRAÇ÷€XAŸ‚«—..pé©@*:yïå‡”BDÀR"AÑHf¢R	FˆšÕ‹#ÁýÊˆ†a˜j>X´(ìûòrûþ(µãùõ/X@ŠaåªTÐ‘×„O"ª5ˆÊM.\Åú,ÎAlãV€\ÕÊ[•®Zâ I´¦6Þµ†2dßƒ#·œ>’èG©Ê[–Ð7lŸ.ÁëqHyJ•É\:¼7åL±4¬—‹±ø/â8T™~0ý\&F[šÚX‘8u<êMêtÈ¾¼øìopM¿Q
o ­^óªuE$Í-µ¿6æ<)ï¥ïŽ¨#­óÖFgùw’ÿ@Ï{Í&Ÿ²/_¢tà.QßJ¥õ?ÓºaéócçP ´Zþ®ç¯”s{9)HÙ‡LÞÎ­—Ï£¬ß!À5çÞG¢Bx'—w^Q(¯|(þ,ó\¤¦…¤lìs„÷ïŠùAzŸ„ßÑ‚«ÁÁ×ª¸ê ²U˜„@5&0.Hû‘Ùrì—¾¾ÆY-Aô€hØ
v9lò‰˜µÀôË='ãîØ}_v[ùoÑ¢ö[
úö¨¿‘W¾*Â}¼‚búe Qéï'ƒ–ÀøÌFNÁºV¸P5Z±æü0}(¸EJÅÜ ´}È¿ ×vØ%/
©@XG‘ûRä.ˆ{‡œ5Z}ÙFØŽq‹¾ñš}±µEgÔOÂ¨ÿ‚Ú²IRsç}áîØX ‰pÖÆ/¶ÿŠX‹Ø#`•³×%Œ³¹Ô£¥ÓYG¯£8„¿C<gïÞÃ3N¹´×îýŠ`^n)„|(å½€†¡‚XQO–>øÆhQÎ»Y ”ãg¸–}±Î#ìÒÜ…ÖÇzk˜–¥&ºR8özeòH™ƒŸ‹	zˆ	$“¸9æÉ<¶e_ýå0W‡Ùl‚ë„^%ò5Û
šãÓóÊP¢¾øí÷—§ÄÍ
·Vsøu¸	ilÈ®ÍX:Ã:ø½ZqÇµÄÃ»CDBk¿BØ†­‡fT -l%Ôr¥š€Ã	æÔ€yÇÌXÌøB‚¸V1ŽÛþþ=ÆZB¹Ì” ×ðåSd’¿".‡ªÚ]Àï”¶‡éERE1zgª>çD5íë™-lÃ
DœŸ‡8ŽÏÙwê×XjcÇÕŒæ{^S¤ZÿãîÅ`üg§Ù§¹xéÙRUJ“®A
›r+€—ì*™Hø7´5PT/7“jn¡Ðfv¦Ë,%OQP n1 =ô#Ï-Rb@åÚ÷lÇ…Šþ#Áÿ}¯ÈuÜ°Z"ºõÉPN;+R'ÞÝyçlå/%8álÉWãÎ/º¹zs3¡Š}÷öûë_~üØ'j,g:k7œù{R¢ã0ïFu1Ó
ÛÚ|b¡ÒX”ÓEðÿ–* +ª_[UF¸ÒÀlËkJçU’§W7¶Lã€fI>5l`Ù7ð#ö:¾©Fuã·Â,öQ\Í—W7a†Ãês˜A¼ëÐÄÆfZµ¤Ÿ/\GnÒÜ>’ÓÄ¯šÝ4ZñolÂìy«…±=Ö¦T?¿5]2
Z+àvîi»RIØxšù&bBµ©ß$õ-¸Þº"€G¿`ë–Ûª}±Ê-‹=6Ügã²Îá&\fTÃoŠË4ß	‚5:‡®ÞþbÅ¤ÌHÑ¥39Þ•yÁ&2~56&í¤Éà|ƒh ñ Ø¶e]€b:^QŸ&¯½÷>5Žß}\ˆW¼Â}ç¯B¿B5_ÊDr&×¼Ñ“{R¹jjR2ßŽ¾•*ø¶å–K÷_¡nŸ5BÁâû
h_ "iŒ–~@¨[2ŠAñ÷Ñ¢îËzØ¼Lv¥´Ð–oŠîsšô*È5xh¸$,Err]*çË"ô½hlœÀï•
 ËV8²	Æ–á-¹!j\±ï vqëSô·n*Ìù*/Ú†1UúÙ×ÕT1ôì¦ö¤ìåƒïÈ#MûËsY´ó^À­ž$9¶RÅT¯:Yùg HK5Ôd'1k€ÔÙ”	­‚…žoŒ	èyÛŽ6§nppÔª%§ÎæB¸7¨‹ºÕ³Î¯¸óeo3nï ·qª=£ŽUÃªHë›ì‰ÈðÄ÷ØÍï›Îþ§…þôÝ#8•ªxéÞŒc¥f`b+fÔSì»Ð]´˜Ü}¨°¬^uDêûo1…ôQfÇñáúç‘ªM•z$TJbwÖu–Ý%-l +}ToÇ?/GÏ®aW¥¢í`~!|yOø*áEÇÒS°Ô¯ø~ïÂÐkå³4ì¸·ëµ:6œÈ[@êÖ,B©õŸc6bq¥¶´@e•f-ª†žÚ ‚ezå÷	cW?Õà‹bºßþÌIŒÎ9Òê’ëúAÌð#}‘¸	¶»…í–ûÚå­†ì­1ÚÔÏà“1eá"²“»—m]‡ÞýÑã¿=~tðÖðØƒ@ÝAwÃ[Ïã¥Ü{˜Ý}Œ®Ãíá&wØZXì7·óìvxB¦¶%bfUÃ>ë°z˜y=ªƒCÌJŸ_ùñ¤¬ªÞØûÓF¥‹ƒlªêA&õ œ*v5ÆÆ@ÐÊ¨+Í·4Û
Ü6DÅ±«ÒµJ; Ç{Õi-ø;§`;Ìÿ”)v>Jív þ#ìJƒÓ–q‡ßÅAê¶“VtÜ½`ëŠ»w‡šÚáUCWç¸Î;ØÎöae £ Áó{°ç4ˆ-gÒ	[ðD¬À³+°­zœ’¾ pÌ…ß±åI" S*Õ~œž³^¤é^].\7‚èAýÛ‚Žà[ÛŒÿÆÈôÍ‹o¨ŸùM8†s¾ðwßÕªséè”Œ^Yô­Ãvµ²èUçÊ´¥Ñs:á ¸¡H\Õ+m×[SjJ ‰öö½uü9)Î„=÷âåoNÊÒñAœÙÂ”HÜ}8s'S6dU¶vòE]NL]<u¶D$Ø˜°½|d–Á”í§Ò~:}i{JO¤½¢c| tüÛú³Ì<ø„ü£ÆUñ# ¶¯mMõÇ±vø>&¾<!€³ÀÿÈcð%{A0FØŠÛ$+­\ˆ>hk
®Èpåèy¾—„Ú_ñ 5ïÓuÑ‰º:œJÉ¢î±T¢Óª|?ìLdá¬¨#rÈÞÞJˆ~$rý ÔpPmó`	Lí'ºáL¯ðÈ:½Á–œš»#QLÍÉœªck·?†ÓùSƒÅ”ÑÃñcCE9|Ø&CïGâÆ&êùŒ¸ëèÞ†ù½Íÿ]àŽã{ï8³·qÀs-ý$ÜèäŸi    0707010005c80a000081b40000000000000000000000015ab2175400000e0e000000660000000000000000000000000000004200000000root/usr/share/doc/opensvc/template.service.container.kvm.conf.gz ‹      í][Û6~Ï¯ N°¶;“m^¦M°³m
½d¤Ý‡¢ðÐe–HU¤ìñbüžsHJ”={2ãÙq*If,‰—ó}çF1ÏŸ?äçÙsö€l.ÑÊr©D5dv]
¶X÷iîaG÷°²{ö{3Ùçg<ûšæûgüìŽ}!Ö+]¥í/\=àed]òi.šž>V5öÃ*ñg-+Ñá{žºRVz)ÔJªÙEçJ*2^ç¶µfDµ”‰`RÍE%-W‰ï4<coÙ\ð4ŒH0˜¼¨ÜuSŠDf2{f&ôf¸ãƒ°LfÔU#S6×Æb‡L–Ê,•P–e•.6nÄ›ÆÐÞ×t÷›íï€#tprü¢ÕQ`ø8Q+!Rf5›¢X–²²5ÏYÁ“9ýfp"…m`
4 5O¾a™Ö»°Ñf™L*­í¤ävÞ#äúÁ¡ñ¾êÃoß2>C• ¤2–ç9 ·”œ•<YÀ%ø¶«)`“á¥’W€„*jc™Ò6À®+úx0À¹ÆìÌÕQç54ÊÜµåoÇ;²:Ï6b	‹¾ôcmJ1º®aˆ]È_êÒÂ¡àÂ.žÌja¬6m‚0D#•)·Âø.j%¯ÙÙJªT¯Ì†–Cƒ¥¨8AcÖÆŠ"PeCÍ	/b Ý>‰à>X· m!.<¢-uÒyÆƒ „:R£&i–Tëª(I<Èòvi/{i·Ò¶¼šB±˜¹¶_&9tF¶ˆ‘Añ2öH‰U+öÈŽ¥Ò,X&so•°¹ý8è¬G¢E"@ ÉX–T‚‡DŠ„ÎnÇBéIY	Q”vÂ§ºzkô ~¡¹r,Ç€£ÇÐô‚_Š*ÁTkèJm†!°ðü`ØD +‰Ð:‘Ã˜Œ1`ìÎE+¸
˜ó8àðwcˆV&Ä WÞ\Äô³‚EjÚixÔœ,Ê\WPw‰jŠaL]–ð(E;,ç`H
öB˜ë—ãv2QCƒå1À°‡vÈ›dø ‡ñÄJ˜!10G·¨FQ[  ŠjØ”é¨»ÙØ°ƒÆål’”õgÕÜµ© z¥¦mØ¹ÊI9Ã@åwó‚^æ¹^5IŒ‚JCY@€R¾vY¡ë€CñŽÙ·ð7ãU'ŒmnáÀi(®¸‚pú‚Ï‡¯ð‹³Ñ+ÄÕãpâ»Á,DÑƒy<0A¼ºZƒæ¦ä*šŽ~Ž¾G˜ï.â¶\`ÀÄÌ¡ùâÛ!¾ÁÙHeÅ~ÿ¢R"ƒõé&rˆšÓ![Íe2‡Ÿ!KÀ×çg¯¾bNþ`ÛUÇÝ 4£©®5FB^ƒNbÈ Ÿ¬+ÑY%J5?FæÀ¼
¦^Ú8Y¦>ÐSA«hä•SÄÓ&ð	x#Y‚HÐC4äŽw«,†Q…q8gÑ¡°ïõù>öýYkË{òj_0Ã,X#¨˜+$„¼&|JÍÜzF0A³J×%®ˆ}Ç ¦5+@®°JÂ§FçµË%Ð­¦©íoZ³ïÁ‰kŽÎIô“Tõ5Kè	3d£s°zBžZå²û¦˜)nMëårk,îAÊêb
ôƒé2©´  ©‰E5'8(ÈMêtÌ^Ÿ}ñÌj][ð…p”ækÝXðžW;¢ÖìJ»{cÎ‘‘ò®õÛ=êDëb‚yC¥óžü·’ÿ@Ë{É²œÏØ‹3ô‚ç/ÑZ0—(ƒ_ABæ~¦5ÈÚ"æÞÇ. è$oÜÏ=_)æví¤ÐúË!Dòfa\ûÜZŠúqAbøºp8jÊ»w2ÉÐóš\y°¡ø³,
‘J˜vî’²©‹Þ¿ÿ9jÈÒYl¸âGƒƒÇ‚ßñ	ÙÚÏ\ ·ÝÈL=uËh_ã,¼”À{€·l;·ñDÌZ`úùŽ“r÷ì¾+»üèPû9}sˆ×ßŠ+_Há>]C2ýÒ“¨¶£÷Ù¨£¡l%g ]3\ÈØ0~>”Ü ¥bnÚÎåŸÑg;ÌŠ—¥TÐXO‘»Rä&ˆûœµRõmÙ–ÛŽq‹žxÃ¾ºµe¯ÔOB©ÿ†[³ÝbhÓ³¾p§m*€D8ëÊ/Öÿ@¬el0ËÙk¦ùBêÉJÈÙ¼§×ƒ„B¼`?¼‡)æœbi'Ý1ûÁ¼  íJøPÈ{ }±¢™,]÷øÆhQÌ»Y ”å'¸–}¶ÎOXÇ>­Í63-K[uöfeâHY€˜Š-DÁ$nILy²ˆuÙeÌÕb4›àú¡š|Ãv‚fùìôœ2¤¨/~ÿãå1q3¸€ßÉ9Ü:\F³Ëj*mÅ:ø=¬¸ãÚâáÌ¡CÂ¡³Ž6~ë¡´æ·šv¥Ê@‡q÷	aN+Pï8ƒ™Š9_Jh®“Œã¶€ëÀøRKH—¹‚ÀâzA¶|†LrwÄéP¨‡qÝÐî>S›†IðbôÎT}É‰j4Ú72»ö !§g!ÆæÇlÇ;S~oÑ+;®f´Ï9I¹EúñöE¯ü''Ù§¹x	áÙJ¡‚J7 yƒM±À…Kö •L$üëKÐZ(ÂÇÎ¥ZH´™™ë:OÉR”äˆ;@e÷µMÞr‹”Lûží8ŸÑf$ø ï¹‰;àZK„@³î#yÊÃiæµEêÄ»;?Xì¥#œ¯øÚ@Ã¸ó]]½ºûâRÅ¾{÷ýå¯?}5Vsw‹×\ŸTÍèÆ8ŽÇ»•]ÌµÂ9X¨4nÊêÒÛC€á×ÎBU%l]ÁlësJçER¤W¦Nc‡f–I1«ØÈ°oàG¬›|Fuå¶ÂÀ„v±X]\ùŽÃu˜A¼ëÐúÆvZäµ¤›9/\GnÒvµÓÄ­š]µRq_lÃìyÃÂØmÓ%
‹ŸÞš.)­p³p´]«Äï	<Mƒü/1¡ÜÔm’º\ïõUÀ#Ï_ÐÎMÍíä¾˜åÖå²iÝÄp—9åðÛÍåœ«Á„ÃWo5"«stKé\.€wuQº‚!ZI›µœ+6õ$2 Û®¨PL§ë“#êÓäâ¥³àÎ¦Æþ{ˆñŠ\ƒ¸«"VhW(çK¹ OÎä†µ":rgÁTSÁ«o£µM`è;¡‚+î˜t÷Uûø¨WW@û$1ZúFíº”	P’—¸>ˆp×X6ÀBhÒ+¥Õˆ¶|Sª{I:,4Üâ—¢v
]+ëÒ"´½¨NàsµÈò5Ž,Cß2³¯ˆì;È]ìæ]×­Bù9_øáÅCÛR¦ Ÿ}UM¡'§0¡±'¥/\EÙhÚ_^È²÷nÍ$É°Õ*Æ |š`åßž"ÑP‘Ä¨BçªNhÌ×£O@ËÛ5´U–ƒ¡Vvš(a!D‰{ƒº\SÞ¯ÞrëÒÞvÜÎ@ïâTwF=«‡U‘Ô·Ù‘á>ˆû¸Î6üOÓ{üéÙ0*!y÷áÞœc¦VÁÄÖàÌÀ©§Xw®«h1¸û°ŸÆ#]Œ·œAø(s‹ãøpù‹÷HaS¥	¥’Xu™ç7µæ·	•Î«wýŸkGïÁaU…¢[å`nÁ?¼Çý	•<Î=–ºÕWïÝ@èk­\”†âz3WÇ‚™b	HSšE(užq1fÛ,®ÔÖ¨¬Ò¼CU_sB;D°\¯Ý>alàš7\RLýíœÄä”=Ý±^6¹l^$À?©¤«#×"Ár7¿½Ó1_·Y«1{WUºjÞ#À=¦ªªK™0tð¸}	¹ÔÆöèÝ=ž¹íño=èÔ=t x›c¼”{µ»‹Òõ¸Ý_å¶ûÕí4«žªùm‰û¨Y¨oØ§b=V÷S¯ûBÕ`pˆZéÓK?ž”V5{Ÿ¬TzÏ»äÏ¨{©Ô½p
ÜÔA*“>5{ÜÔl'p»9Ç>K{Ô,í ï”§uàïQ|œ„í0eü$Uì!|”Üí@5ü%ìSƒã¦q‡)ßÉAê¶£ft*Ü`ë“»#'w‡ªÚáYCŸç9Ï;XÏöaE /ÁÓ{±ç8ˆ­æÒ
SòDŒŒÀ³+°-¼ÎƒGIHW 8ÂíØò$€IU+Õ}ž³A$éA“.\¶Ñ‹ú×%¾a¹¹ÿM%Ó·/¾¡zæ·ÃñxìÏùÂß]U«.¤¥S2u94ËÕêrÎ•é¶Fïéøc  C‘ØP+m6KSJ ‰öÖ½õü9*N„=wâÅ=oŽÊ’ñAœÙÁ”¨¹»pæF¦lµ*l#ÜÊ]öt92]tùÔÙ‘àvÇ„åå“jõLÙ}zíçS—¶ç TÿFº—;!:Å·RAÇ¿m¾ËÌÓ‘;@È½úPÙà?<jûÊÆðÐTwkïcâË ü?AF¯Ù‚1ÂV\'ymäRAZ30EW–Þç{I¨ýZ3ð=Q¨«ý©TQ[t°2¦JtZ•«‡‹ÜŸUDŽÙ»kéäšÜ< ÕTÛ¾XB	S÷®‘?ÓË¿²N_°§âî¨©¦fe9ÇÒnw§u§‹)8¢—ã§†‚²ø²L†¾š›j˜¨;ä3â¦£{[æ¶ÿ§‚Žï½áÌÞ¶‰ÞkéÍègaFÿX¦kÅ^i    0707010005c80b000081b40000000000000000000000015ab2175400000d86000000660000000000000000000000000000004300000000root/usr/share/doc/opensvc/template.service.container.ldom.conf.gz    ‹      í\ÿoÛ6ÿ½bp‹³½¤·ý’µÅå¶n[h·û¡Z¢lÂ©‘”î¿÷)‰²ãØiâ\ÜS€­‰-=‘ïóyßÈ'>þ?Ïž³üAq‰VŽK%Ì¹u)Xžêââvt«»gŸšÉ>?ûãÙw4ß74ãgÏpð±^i“^´cP¼ðÅèPI6Ñ%Ÿæ¢yÒGSás˜VÒˆv?òÜÒ7¥ÑKi¥VRÍ.:ß¤"ãUîÚQ»¹`V˜¥LD=&Õ\é¸JÂCsÁ3ö–ÍOë	“ÆoK‘ÈL&pÍL€ÆdB*‚§Ù®ø “=ªQ*›këðLZ–Ê,F(Ç2Œê^ˆAÞwtõ›íï€£~ÀÉAò«VGáãT­„H™ÓlŠJl`YJã*ž³‚'sPúÍàDÛÀTßÐ€ÔÜù†eZïÂFÛe21Z»IÉÝ¼G( ô“Gã})Ô‡ß¿g|†&HeÏs@n)9+y²€¯àÓ®¥€S„#”Jn 	6TTÖ1¥]»6ôð`€sŒÙ;˜«§Ï+Ê\É
—ãY•ç±”E†±6%†X]™DX"Fr Ç×ºtðŸPðÅ.žÌ*a¶ÿßaˆF*Sî„¨”¼fÿa+©R½²VKa8Ac×Ö‰¢¦Ê†™.AÅ 
ÝÛô¤4B¥›ð©6îäi¾9$8Z€¤y˜åR˜hS­áQjÓàk;tpÿ`ØØþJæ9*‡1Y	jÀ¨°jß‚	ñØÔÃ•‰.
"LˆéŒñæKü‚þCÇìƒ7M¢73ûwY”¹(À¢éq4y[•%ÜJ~†å|J¡SÁ^{ýrÜN&4ÈPt8$@¸!l’iÜã‰“0Cp0G·0£H(€ü	›r+½SÙ”,ö ì q9›$eõ…ù“;°6@¯Ô¶‚#@ß“r†.âŽùA/ó\¯štFîÜRþ ®PÊ×>óOàP½cö=üŸqÓ	 Í%¸#-!Ã²v6<¾ÂÎF¯×€À‰ì³EæñÀõj³ËM)ˆ7ú%úa¾3¸ˆÛ^p;ñ=Ä·C|C°‘Ê‰™0øÿ!Œy=Øè#¯¬H‡l5—É~‡ü x ŸŸ½ú†yýƒoWpÐŒ¦ºÖX	‰!ƒL®2¢SŸ¥‚Žž±‚¾W/]œ¦Ò30RTtòÞË)“ˆ€	D‚¢‘ÌE­Œ9êŽ÷õ†a˜z>Xt(ìûö|ûþ¬´ã=ùõ/X@Šaþ©TÐ‘×„O"êJ¢vA3£«kÑ¯âÄ¶nÈU×'|ju^¹ºPITÇªm7UBcö#8"qÍ1Á"‰~–ªºf	Ýa‡lt^CÊS©\Òá³)gŠ¥i`½\nÅßˆãPU1úÁô™mhjcDâdã¨7©Ó1ûöì«¿Á5AüE)|€:´~,xÍ«Î‘4·ÒþÚ˜óDd¤¼—~{Dh]L°ª4:ïÉ+ùô¼—,ËùŒ½8Ã(xþ w‰Z°øQ*­ÿªÿÊ!æ!Æ. @iü®ç¯”s{9)H9„LÞ.¬—Ï£¬ß!À5ÞG¢Bx'—O^S(¯}(þ.‹B¤¦‡¤lês„÷ï‰ùAzß„¿Ñ‚ëÁÁmuÜõ	PÙ:ÌB —•üÈl5õìw8‹ %ˆ­ [ÁÎÇm>³˜~¾‡ãdÜ=»ïÊn+ÿ-:Ô~GAßõ·òÊRE¸O×PL¿$ªÜè}6êØ ŒÈlä¬K`…U£ÎÓ‡’[¤TÌBÛ‡ü3úÙÃ»âe)ë)rWŠÜä q€³V«!`¢/Û
Û1nÑoØ7·¡¶ìúIõ_²c[6IjŸ²/\ãž
 ÎÚøáÅö_k{¬röº„i¾z²r6ïéõ áï/ØOïaŠ9§\ÚkwÌ~G0/ H·B>”òž@ãPÁF¬h&Kß|c´(çÝ…,PÊñ\Ë>Û	çg¬c‚ÖÇfƒ‡–¥&ºV8öfeòHY€Ÿ˜Š=DÉ$îBLy²ˆmÙWÌÕa6›àú¡W‹|Ãv‚æøìô‚2”¨/>ýñò˜¸Y\ÀïÔ~.#Ù¥™Jg8B×+î¸öƒxxwè‘Cè¬ã×Û°õÐŽ
¤…­„F®TØ0L`N˜w\ÁLÅœ/%ˆëã¸-àŸ?`|©%”Ë\A	àp½ _>C&ù+âr¨Þ‰ö¡Ý¼§²L/’:ŠÑG8Sõ5'ªÑhß4Èì`V âô<ÄÃøü˜Mxg~¥1v\Íhïóš"Õú_oï Ærš}šûˆ—ž­T­T0é¤à°)·¸pÉ ’‰„CóGEýãæR-,ÚÌÎu•§ä)J
Ä ±‡®‚à¹EJ¨]ûží¸PÑa$ø_ ï¹‰;àVK„@·2y*Àiç•CêÄ»;?9[ûK	N8_ñµÁ¸ó‚®®Àƒ^]C[—b?¼ûñò·Ÿ?‰«¹Î»m#þ™ÔGäÇ8ŽÇ»U]ÌµÂæŸX¨4åtü¿¥
ÀŠúÏÎB•®20Û*ÁšÆy‘éÅ•­Ò8 ÙeRÌYö~ÅŽ¥·õ¨®üV¸0Ë.«‹«0Ãqý=Ì Þuhcc;-ŠZÒO‰‚®#·iÉiâWÍ®Z­ø¶aö‹¼õÂØkÓ%*‹ŸÞš.­p»ð´]«$ì	<M‡üO1¡ÚÔo’ú\ï]À£À_°ÎMËíÔ¾XåVå²iÕäp—9ÕðÛârÁÀw‚`AÎa‰«·¿Y‘U9)º¥t.À»ª(Y&sáWccÒfmçÛ¼É€ŒÀ¶+ëÓéúäˆú4¹xé=¸÷©qüâB¼¢Ðàîû÷úªùR. ’3¹áí€ˆžÜYíª©Õ,Èh}8úNªà›;.ÝßBÝ>!k„‚Å÷Ð¾@MÒ-ý€P·.eƒâ%î¢E#Ü5–°‘ìJi5¢-ßÝç4éUPhðÐpIX6ŠäºRÎ—Eè{Ñ82lœÀû*åkY†±e<f+nˆì¨]Üæý£[ƒ
s¾Ã‹‡¶eLµ~öu5Õ=9ƒ©…=){ùà;òÈGÓþòB–Ý¼pk&IŽ­R1õO“¬ü+P¤£j²“˜5@êlª„VÁBç&Æô¼]G[PO'8jÕ‘Ód	!JÜÔåšêþxõ–;_ö¶ãöz§º3êYõ8¬Š´¾Ížˆ÷a@üŒÛ9à}ÓÉÁÿ4£pÀŸî} §Rï!Ý›s¬ÔLlÁ‚zŠ}7:°‹“»5–õO‘†>Ç[Î }”¹Ãq|¸ü5D¤zS¥	•’Øu™ç7IÛÈJÕ»ñÏÅÑ(ØA©èV;˜_F7ï	B%¼ìYz–úÕßïÝ@z­|–†âz³VÇ†™bHÓšE(uîñ9f+Wj+TViÞ¡jè9¡"X®×~Ÿ0vpÍ{0¾(¦çíÏœÄä”#Ý±^Dºl^$À?1Ò÷‰k‘`»[ØÞé¸¯Û¼Õ˜½3F›æ=Üc2¦*]äÂBvÒÁãö%äR[×£wwôxæ·Ç¼<ö P÷Ð=€ámfŒñRîÌî.F×ãv“{Ø:Xì7·ÓìvxB¦¶%îcfuÃ>ë±ºŸyÝªƒCÌJŸ^ùñ¤¬ªÙØûl£ÒåA6Õu/“ºN5·5ÅÆ@ÐÊ¤/Í·4Û	Ü.DÅ±¯ÒµJ; Ç;Õiø{§`;Ì?Ë{¥v;Ð?ÇûÒà¸eÜaÆw@qÐ…º‡í¨Ýw'ØúâîÈÅÝ¡¦vxÕÐ×yG®ó¶³}˜Eè$hðô^ì9b«¹tÂ–<#+ðì
lG«_çÁ£$¤ï áwly’ÀÄTJu_§çlizÐ”—­ zQÿºÄ7,77ã_™¾}ñšú™ßÇãq8çÿö]­ºŽNÉTåÐ:lW«ÊA}®LW½§Ž€ŠÄÕ½Òv³5¥¡hoß[ÏŸ£òçDØs'îPÐóæ¨¼!Ä™L‰ÄÝ…372eKVÝaÑàV¾è²§Ë‘é¢Ë§Î–ˆ·&l/Ÿ˜Õc0¥ýîKÛsPjx#=èâÛ © ãß6ßeæéÈ ä_}0®Žµ}mcxhª?ŽµÇ÷1ñå	) œÇ?ú–½ #lÅu’WV.Å´5Wd¸rô>ßKBí¯xÐš…ÏéŒºèD]N¥ŠdÑ9ÕX*ÑiU¾v.òpVÔ9fï®¥?
;¹y j8¨¶}±„
¦î]£p¦Wxe>`+NÍÝ‘¨¦ædÁÕ±µÛÃéü©ÁÀbJŽèåø©À¡¢¾ì“¡Ï#qSõ‡|Æ@ÜttoËüÁöá7ß{Ã™½­ˆÞkéÝèáFÿeÊ­Ùd    0707010005c80c000081b40000000000000000000000015ab2175400000e5f000000660000000000000000000000000000004200000000root/usr/share/doc/opensvc/template.service.container.lxc.conf.gz ‹      í]mÛ6þž_A (œàlg7×|Ù6Áíµ)Pô%$í}(
/-Q6±©’”½>Ü¿™!%QözíÍÚÛuO’ìZÒˆœç™7r¤<~ÈÏ³çì€—hå¸TÂ™[•‚å7ÉCÄvt‡ÕÝ³ßšÉ>?ûýÙW4ß·8ãgÏpì×bµÔ&½h‡`’"…£~PG6Ñ%Ÿæ¢¹Ó'S	<`Ä•4¢Âw<·t¤4z!­ÔJªÙEçH*2^å®õÏZÑ©æÂHÇUn”ž±wl.xZB0˜°0þ¸-E"3™À93J’	ž:[É·ó\ÜÂàÞ6—ª¥Ì¡ÐNÀEÁUÊ4\ld*˜›ÆA®«G"¾B{FÜ9žÌÙH°‘bÚ._(œõÙ9èŸmCHñBœB¨
+ÌB&¢žÀaÐŠ0ù(“ÝªÅf®­Ã2iY*³LÄ#3ºX;O#@töÛÍo£¾ÁÉArP£‰`ø4U+!Ræ4›¢XÒ¸Šç¬ ÚƒÒo'r©LõHÍ•oY¦õ6lÀ¦’‰ÑÚMJîæ=B¡ï=J¡>þúMpQ TÖñ<ä’³’'×p¾íZ
DM@8B©äp`CEeSÚÕ°kCÿ 8—Á˜½‡¹z*ð¼¡\Á¹‘¬p:ž‘UyŽ°K@Y‘;mJ±º2‰°DŒ.ä@WºtðG(8°'³JX§íÿ7A¢‘Ê”;aÃ-*%oØÙRªT/íš•ƒÀRNÐØ•u¢¨©²fæ„KP1 ‚B·f \ö(84JÝ ¢Fh‡úÀ£Ù)4K
Eµ°}¤‰ÓfÅôZœ£Ì‚”w·GL²?W¹ÛYþèÚýVd 9Ë8  ÉLÎ*ä³V,“99>ÞLÀÙ¨”›”å:¡ãÄè$CïbÍâU±jpxåålS?I™ƒõ_cxeòšÓµŽÀQ`'/U	QÊí-‚€h.¸“ùJOJ#àT7áSmÜÉ9ûæÈ±¼=Ž¼}sŸ2hª5ÜJ­çuˆwpý`Ø¤K™ç,¨Æd%¨joaK	Ñ™ÇYD8³®¢0ÆÃ„¼9ˆè"8f5d€$Úq3qê(¢€dn®SSbb«²„K)…a9‡t¥ÐP©½öæå¸L$h¡>˜Ë á†l°N¦AÈdOœ„i5z£H(€R6åVzò®K{ ¶Ð¸œM’²ú‹¥*÷`m*€^©m'F€¾'åýõo8æß»•zžëeSÉÁˆ!S´TšH¬ÕU¾ò¥ž¿ ‡ê³oàoÆM'7mNáÀi)¿5\AŽ|ÁÎ†çÃ×øÅÙè5âp8ñ‹í`¢èÁ<˜ ^L—9Ú[s£Ÿ¢ïæ{ƒ‹¸í0±sßC|7Ä·úbF‰mÿÂ(‘×ƒ5$8òÊŠtÈ–s™Ìág(=€ðõùÙë/™×?øvÕ	7 Íhª+`•P¬`2(+#:K?©‚ ã‡{@¬ £àê¥‹+`ºF*ŠNÞ{ù1I°‚H@4Â¼2¨#DCŽúÃ‘à~éDÃ0L=,:”ö½9ßÅ¾?*íxO¾}ýbT
 ¨0 '"¯	ŸsÍ‘Ö.hftUâ2×qb[·äª—>øÔê¼rõH¢%2µ)ð¶E–1û‘¸á˜à‘D?JUÝ°„®°C6:¯Ç!å©T.éðÞ”3ÅÒ4°^.6Æâ/Äq¨ª˜ý`ú…LŒ¶4µ±
"q—±AoR§cöæì‹À9AüF)|:´~,xÎëÎ‘4·ÔþÜ˜óDd¤¼—~wDh]L°X0:ïÉ'ù÷ô¼—,ËùŒ½8Ã(xþ w‰Z°øU*­ÿ™+‡˜‡{u Jkägp>|¥œÛËIAúË!dòöÚzùÜ9Êúò\sá=p$*„wrÉpç…òÚ‡âÏ²(D*aÚyHÊ¦>Gøðá§H¤÷Øp$üŽ\.«ã¨O€ÊVa Õ˜À¸bíGf«©_û
g´Ñ¢`+Øù¸Í'bÖÓÏwpœŒ»g÷}ÙmåD‡Úï)èÛ}¢þF^ùBª÷é
Šé—D•}ÈF€ñ™œuÑ²TV¬9?LJn‘R17mòÏè³ƒvÉË×·úÔóÞ¹Íâæ"g­VCÀD_¶¶cÜ¢+Þ²/ïBmÑõ“0ê¿!dÇ¶l’ÔÞ9d_¸}6@"œµñÃ‹í¿&Ö"öXåìt	ÓüZêÉRÈÙ¼§×AÂ?!¾fß€)æœri¯Ý1ûÁ¼  ÝRøPÊ{ C±¢™,øÆhQÎ»u¿LXÇOp-ûl+œèÖ¹­OÍÞ1-K;Lu­>pìÍÊ,ä‘² ?1zˆ’IÜàœòä:¶e_ý0W‡ÙlÒì
Õ"ß²­ 9>;½ %ê‹ß~yLÜ,.àwj¿—‘ÆÆìÒL¥3¡ƒßëw\ûA<¼;ôH„!tÖñk„mØzhGÒÂVB#Wªl&H0§Ì;®`¦bÎÄuŠqÜð÷0¾ÐÊe® p¸^€/Ÿ!“üq9T7¹øÛÐî^SÙ¦IÅè+œ©zÅý¾ŽömƒÌ¶a"NÏCÆçÇl‚À;ók,±ãjF{×©Öÿx÷†b0þ“ÓìÓÜG¼„ôl©j¥‚I7 ‡M¹mÈ§`•L°Ã2ô•µPÔ7—êÚB¡Íì\WyJž¢¤@Üa {hX
ž[¤Ä€ÚµïØŽý_Œú^‘ë¸.`µDtë!“' œv^9¤N¼»ó½³µ¿”à„ó%_YŒ;¿ èê
<èÕÕ8tŒ*öíûï.ùñÓ¨±œë¼Û‘æïI-Š~Œãx¼ÕÅ\+ì{ó‰…JcQN—Áÿ[ª ¬¨í,Tá*³­¬)aœI‘^\Ù*š]$ÅÌ°‘e_ÃØù®Õ•ß
³Ø]qq½¼¸
3×Çañ®CÛiQÔ’~J¼p¹H{ûHN;¿jvÕjÅ±	³_ä­ÆvX›.QYüôÖtÉ(h­€ÛkOÛ•JÂžÀÓtÈÿÒ0ªMý&©oñÀõÞÐ<
üë\·ÜNí‹UnUî°á!›VM—q™S¿).×|'ÔÂ÷ÜÙ/VdUNŠn)Ëkà]U”ÔÕåWccÒfmç;HÉ€Œ{À¶-ëÂ–±éêäˆú4¹xé=¸÷©qüâB¼¢Ðàî[ƒúªùR. ’3¹æí€ˆžÜYíª©‹5Èh}8úNªàûš;.Ý_BÝ>!k„‚Å÷Ð¾@MÒ-ý€P·*eƒâ%î¢E#Ü5–°»™ìŠZqË7E÷yšô* ‡Nà”°lÉ)t¥œ/‹Ð÷¢qdØ8Av
 ËW8²cËxÌ–Ü5.Ø·P»¸õ)ú[·æ|†mÃ˜jýìêjªzrS{RöòÑwä‘¦ýåkYvó^À­™$9¶JÅÔŸ&Yùw HG5Ôd'1k€ÔÙT	­‚…¦pŒ	èy»Ž¶ vqpÔª#§É®…(qoP—M‹s“_qçËÞvÜÞAoãTwF=«‡U‘Ö7Ù‘á!ˆïq7¼o:9øŸføÓµp*uñÒ½9ÇJÍÀÄVÌ ¨§Øw¡»h1¹ûXcYšˆ4ô9Þbé£ÌŽããåÏ!"Õ›*ÍH¨”Äî¬Ë<¿MZØ&@Vú¨Þ^ (ŽnÃ®JE7ÚÁü2B¸xGø*áeÏÒc°Ô¯ø~ïÂÐkå³4ì¸7ëµ:6œÈ[@šÖ,B©sÏ1[±¸R[Y ²JóUCÏ	í@Ár½òû„±ƒkoðE1Ýowæ$&§éŽõdÉeû86dø‰‘¾HÜˆÛÝÂöNÇ}Ýå­Æì½1Ú4Ïà“1Ué"²“w/!—Úº½û£Ç3¿=~pðÖðØ@ÝCw Ã[Ïã¥Ü{˜Ý}Œ®Çíá&wØ:Xì6·ÓìvxB¦¶%bfuÃ.ë±z˜y=ªƒ}ÌJŸ^ùñ¤¬ªÙØûl£Òå^6Õõ “zN5w5ÅÆ@ÐÊ¤/Í·4Û
Ü6DöÅ±¯ÒµJÛÇ{Õiø{§`ÛÏ?Ë{¥vÛÓ?ÇûÒà¸eÜ~Æ·GqÐ…º‡í¨Ýžw/ØúâîÈÅÝ¾¦¶ÕÐ×yG®óö¶³]˜Eè$hðôì9bË¹tÂ–<#+ðÝØŽV?Îƒ¯’¾ p,„ß±åI" S)Õ}œž³A¤éAS.\¶‚èAý›Ÿ°\ßŒÿÚÈôÝ‹¯©ŸùÝp<‡÷|áï¾«UÒÑ[2U9´ÛÕªrP¿W¦+žÓ	¯€ŠÄÕ½Òv½5¥¡hgß[ÏŸ£òçDØs/îPÐóæ¨¼!ïÅ™-L‰ÄÝ‡3·2eCVÝaÑàN¾è²§Ë‘é¢Ë§Î–ˆw&l/Ÿ˜åc0åsÞ|j}i;^”žHz'D§ø4@*èõoëÏ2ótä_ ä}0®Žµ]mcøÒTÿ:ÖßÇÄ—'¤ pø_±ŒÞ°c„­¸IòÊÊ…‚¶fàŠWŽžç{I¨ý_´fá{zG]ôF]ÞJÉ¢Wàc©Do«òý°s‘‡w`E‘cöþFú·ìG"×_€^TÛ>XBS÷‰®Qx§Wxd¾`KNÍÝ‘¨¦ædÁÕ±µÛ¿†Óù·‹)9¢‡ã§†Šrø°L†¾ÄM5LÔ¿ä3â¶W÷¶Ìlþ÷·¼¾÷–wö¶"öx®¥w£	7ú?áˆÕj   0707010005c80d000081b40000000000000000000000015ab2175400000df9000000660000000000000000000000000000004200000000root/usr/share/doc/opensvc/template.service.container.lxd.conf.gz ‹      í]mo7þž_A (”àdÕÎµ÷ÁM‚óµ)\Û´÷¡(dj—’ï’[’+Y‡ûñ73$w¹’eÉ±åZ½5&Ö®fÉyžy#gÙçÏòçÙsö€?(.ÓÊq©„2·ª+®óûˆ{ØÑ=¬îžýÚLöùéoÏ¾¡ù¾Á?{†c¿«¥6ùy;ÅKNðud3]ñI!š'}25>‡ñ{-h‡ð=/,]©Œ^H+µ’jvÞ¹’‹)¯×ŽÚÍ³Â,d&â˜Tsa¤ã*-Ÿ²·l.xG$L^ÝV"“S™Á=3
“©žf3¸ã£pLNéQNÙ\[‡dÒ²\N§ÂåØÔèríF¼iò¾¡»ßlŽxñGÉOZ†OsPµ"gN³	*±e!«yÁJžÍAé7ƒ“lSüBRóÍ7lªõ6l´]dc£µWÜÍ{„Bï=*¡>þò-ã34	€@*ëxQ rÉYÅ³+¸Ÿv-|2 œ TqH8°¡²¶Ž)í"ìÚÐ_ÀƒÎe0bï`®ž
¼¨A(Wpo"+ÜŽwLë¢@Øˆ% ,ú0Œµ(1ÄêÚdÂ1º=¾Ô•ƒ?BÁ…m<™ÕÂ:mÿ¿	Â\æÜ	Q+yÍþË–Råzi×¬VÂp‚Æ®¬e¤Êš™.AÅ 
ÝDÁk•ÍÇ²œÿP4â…0â…Gt§>ÿ! mùU&'¦U;BRsª@Ôõ¤V®>?ûÛèô«:3!OÕ¶ªý!é¯ÕB˜D¶âzŒó¬ÌÏ//‹ë,¨Ž½&u¿e£Ñˆ½Æ¨ôöò’}z?¸¦xÏ¶5ÙÓãÊQVnÌ'Ú¸'
ÏvÕ\9”ÂÑ‚jž³âDkx”Z€109øþ`ØÃ¥,
Tc²Ô€i G¤à*ÄžÆ¾pg¦ËFFÆ	bzÊxs/ÐDpÄ>jÈ[H´ãf&Ò„G–U!JqÜ#×Nm]UðU
¼@:²¥Î{!ìõËQ;™DÐ`Šú`&ÂÙ`LƒÏœ„B<…9Â¸…9Id(À²	·ÒGÙuIÀ`ÀW³qVÕOÕ¹|f€½ksôÊm+83ô=®f3Å1ÿÖ!èEQèeSÀˆ!¿±”PC,”Š•/PüÓ 8Tïˆ}ÿeÜt2ªæÜ‘–²2ÃÄ‡sv:<¾ÂNO^!®'€?Øf)ÊÌÃ	êÕf–›SVÛ<èÇäs„ùÎà"n;ÁŒíÄ÷ßñÁF*'fÂtàÿ§0Jq°¡òG^[‘Ùr.!…¨-$ÌÀøøìôÕWÌë|»ê„€æd¢k`•bc2(mj#:¹‚ ã‡g@¬ «àê¥Kë6zF*ŠNÞ{ù%90ƒ‡HA4’…ˆ*ÁÑ#þp$¸/ø5ÃÄqø`Ñ¡°ïë³]ìû½ÖŽ÷äÛ×¿ `)†™FPa0@OC%â“a½Jëè‚fF×.Î|‘æ ¶u+@®X°ó‰ÕEíbå’haGm
¼ii`Ä¾G$®9&8C$ÑRÕ×,£oØ!;9¯Ç!å©U!KéðÙ”3¥Ò4°^.6Æâ¿ˆãPu9úÁôK™mhnS$â”§¨7©óûúô‹¿Ã=AüF)|€:´~,xÏ«Î‰4·ÔþÞ”óDd¤¼—~{Dk]Žq™Åè¢'ÿ­äßÓó^°iÁgìÅ)FÁ³—è¸KÔ‚Åriý¿i9¬vˆyˆ±WP ´Fþîç¯”s{99H9„LÞ^Y/Ÿ;GY¿C>€k.½ND…ðN.ž¼¢P}(þ[–¥È±À/Â@r6ñ9Â‡?&‚ü ½Ç†+áw´à88øZŒ; >*[…9@h TSã:«™­'~EçœEÐDˆV€­`g£6ŸHYL?ÛÁq2îžÝwe·•ÿj¿£ o÷‰úyå©Ü'+(¦_ÕîäÃô¤c0~ ³‘3°..TV¬9?L*n‘R)7mòOég;ì’W•T ¬§È])r“Ä-1ÎZ­†€‰¾l#l§¸%ßxÃ¾ºµEoÔOÂ¨ÿ‚Ú²IRûä}á¦ÏD ‰pÖÆ/µÿH¬Eê°ÊÙé&Å•Ôã¥³yO¯qÿ€,øŠ½ÿ S,8åÒ^»#ö‚y@º¥
ð¡”÷ …
6aE3YºðMÑ¢œw²@)Çp-ût+œ°ÙpZŸšOZ–v˜4ê¨>pìÍÊ,ä‘²?1SôSH&q[nÂ³«Ô–}õWÂ\f³™‹ûQä¶4ÇgÇ”¡D}ñëo/‰›ÅüNÍá×á¦¤±»0éGèà÷¸âŽk?ˆ‡w‡‰0„Î:~DØ†­‡vT -l%4r¥š‚Ã	æÜ€y§ÌDÌùB‚¸N1ŽÛþùÆZB¹Ì” ×2ðå3d’¿#-‡bk†í.àwj;Àô"‹QŒ>Â™ª/9QFû¦AfÛ°Çç!Æç§l‚À;ók,±ãjFû=¯)R­ÿçí-1ÁøN³OsñÒ³¥ŠJ“n@
›r+€—ì*™Iø;tCµPÄ7—êÊB¡Íì\×ENž¢¢@Üa {h³	ž[äÄ€èÚwlÇ…ŠþOF‚?}¯ÈuÜ°Z"ºõÉPN;¯R'ÝÝyïlô—œp±ä+‚qç]^‚½¼…>GÅ¾{÷ýÅÏ?|5–s]tû¨ü3©±Îq”Žw£º˜k…ÝZ>±Py*Êé*øK€ñ×ÎB•®60Û:ÃšÆé%l§Í.²rfØ‰e¯áŸÔ,Gué·ÂÀ…Yv~µ<¿3Åë0ƒt×¡í´(jI?%
^¸ŽÜ¤}|"§ˆ_5»lµâ?Ø„Ù/òÆ…±Öæ»<øñ­é’QÐZ·Wž¶+•…=§éÿ¥!`"&T›úMRßâë½¡+xøÖ¹n¹Ú«ÜºÚaÃC6©›nÊeA5ü¦¸BC0ð XP£sXàêíÏVLë‚ÝRºWÀ»º¬ØTÂ¯Æ¦¤¶œï{$2îÛ¶¬PÌ'«£#êÓäâ…÷àÞ§¦ñ{ˆñŠBƒWxìéÞÁ²œˆäL®y; ¢'÷4ºjê½2ZßŽ¾“*ønÜŽK÷_¡nŸ5BÁâû
h_ ’ƒ4FK? Ô­*™Å xIûƒhÑweìÉ%»RZÐ–oŽîó
4éUPjðÐpKX6Jä”ºVÎ—Eè{Ñ8¦Ø8ß«@V¬pdSŒ-£[rCÔ8gßAíâÖ§èÝT˜óy^:´cŠúÙÕÕzt…=){ùè;òÈGÓþò•¬ºyoÚGIŽ­V)ñ§IVþ(ÒQ5ÙIÌ u6uF«`¡•czÞ®£-©ÉµêÈi²„+!*ÜÔÕŠêþtõ–;_ö¶ãöz§º3êYõ8¬J´¾Éž„÷a@úŒÛ9à}ÓÑÁÿ4£pÀŸ¾û N%ï!Ý›s¬ÔLlÁ‚zŽ}7:°‹“»ËøÓD¤¡Ïñ3HeAMâ/~
)nª4#¡R»³.Šâ&ia› Yé£z7þy 8z%»"(ÝhóËáË;ÂŸP¯z–‚¥~õÀ÷{7†^+Ÿ¥aÇ…¸^¯Õ±áDæØÒ´fJïø³‹+µµ*«¼èP5ôœÐ¬Ð+¿O˜:¸æÅ0_ÓóvgNb|Ì‘îP/û\4/`†ŸéûˆÄµÈ°Ý-lïtÜ×mÞjÄÞ£Móî1SW.qa!;éàqûr¥­ëÑ»;z|ê·Ç¼5<v P÷Ð=€á­gŒéRîÌî.F×ãv“{Ø:Xì6·ãìvxB¦¶%îcf±¿a—‰õXÝÏ¼îUƒÁ>f¥¯üxRVÕlì}¶Qéj/›êº—IÝ§ˆÀí@M°1´2îK³Ç-Í¶·‘}qì«´G­ÒöÀñNuZþÅÇ)Øö3ÆÏ2ÅÂG©Ýö4ÃÏ1Â¾48l·ŸñíQt¡îa;hE·§ÁÝ	¶¾¸;pq·¯©í_5ôuÞë¼½ílfI:<¾{ƒØr.°ÏÄ‰xv¶£Å×yð(	é{  ÇRø[že01µRÝ×é9$š4åÂE+ˆ^Ô¿®ðËõÍø×Fæo_¼¦~æ·ÃÑhÎùÂß}W«.¥£S2u5´ÛÕêjÏ•éJ£÷tÂ1 ð@‘¹Ø+m×[SJÜr(]ÏŸÇáÏ‘°çNÜ¡< çÍAyC:Þ‹3[˜’ˆ»gndÊ†¬Øa›ÐàV¾èª§Ëé¢«§Î–„·&l/›åc0¥ýOÜ—¶ã ÔðFzÐ;!:Á·rAÇ¿­¿ËÌó€õÁ¸?j»ÚÆðÐTkïcâË3R 8üßSœ|Í^Œ	¶â:+j+bÚš+2\9zŸï%¡öW<hÍÂçtF]r¢®§R%²èàv,•è´*ß;E8+éˆ±w×ÒŸŸˆ\? 5TÛ¾XBS÷®“p¦Wxe>`KNÍÝ‰¨¦ædÉÕ±µÛÃéü©ÁÀbJŽèåø‰À¡¢¾ì“¡Ïqõ‡|¦@ÜttoËüÁæ¡ù7ß{Ã™½­ˆ=ÞkéÝèŸÂþò§–lég     0707010005c80e000081b40000000000000000000000015ab2175400000e3a000000660000000000000000000000000000004800000000root/usr/share/doc/opensvc/template.service.container.openstack.conf.gz   ‹      í]moã6þ¾¿‚À¢ð.Îq“½öKÚ]\®Ý‹k»ì¶÷¡(Z¢lÂ©’”îÇßÌ’(;~É&Nã­´›Hòˆœç™7r¤<þŸgÏÙ~P\¢•ãR	3dnU
¦K¡¬ãÉüÓÄ=ìèVwÏ~k&ûüü÷gßÐ|_·3~ög0«¥6ée;Å'Îðƒš²‰.ù$Í>š
ïÃŒø£’F´Cøç–Î”F/¤•ZI5½ìœIEÆ«Üµ£v3Á¬0™ˆzLª™0Òq•„›æ‚gì›	žÖ#˜TÞ–"‘™Làš© µÉ„Tw³	\ñA8&3ºU£Y6ÓÖá™´,•Y&ŒPŽeFkâE#÷]ýzsÄ[à¨oprü¬ÕQ`ø8U+!Ræ4› XÒ¸Šç¬àÉ”~;8‘Ù60Õ_h@j¾ùšeZoÃFÛE26Z»qÉÝ¬G( ôÎ£ñ<Í‡_¿c|Š&Ht<yÈ-$g%ø 8G»–žŽP*¹$ØPQYÇ”v5ìÚÐ?ÀƒÎe0boa®ž
<¯@(Wpm$+\ŽWdUž#lÄPcmJ±º2‰°DŒ.ä@/ué¾$§ºH¶ñdZ	ë´ýk„!©L¹6Ü¢Rò†ý-¥JõÒ®Y9gÃ	»²N5UÖÌœp	*@Pè6 ìŒƒNÆ²O®Ê¿6 kÚNr]¥ACL–Œ4D-ÌA' Ã­&9H‚KÐ:HûkzÝí5­üoÍ65¿(H3CÆ-›+½Tµ£ò§®À9pq7¼(qš`+¯}øÚn•Ã‘ñŸžDÔ'žˆÖA'Äî=
ÒQƒ‘Ç»Ÿ82Õ3Áæ-¡Öôn<èc™öx¬[LH29­È)ˆø©á¡ˆütõóÒy­ÍÝ:Wz\!ŠÒùDwr.¨9s¬pŒ£…pÜÜT¾&ÁDk¸•ZOöê¬D†MÞ·”yÎ‚ÊaLV‚°â®œ…ô‰wŒÉ_™èL/¥$&Ät†1'œÄô"8b4X‰vÜLEœÛKäIÙÝòM™£­Ê¾J9&Ë9Xuìb/„½y9j'	d¨&›$@¸!¬“iRMÆ'a†@\˜#Œ[˜³H(€rI6áVú„r]Ø°…Æåtœ”Õg–KÞµ© z¥¶œú—St¿á˜ïô
2—eSjÃˆ!•·T;BÚ	(å+_‹û»p¨ÞûþÏ «‰‹‡æpQ¹´T€® ˆ¹dçÃ‹á+<p~ö
q8œx`;˜…(z0&¨W›…´·æF?EÇæ;ƒ‹¸í0¦Ü¸‡x7Ä·©œ€ü«ÿ¿„Q"¯Š|pä•é-g2™ÁÏPàðÅù«¯|EýNuÂ@s6Ñ°ÆJ¨&1HTñttÖæRAÇ÷€XAgÁÕK/QÐ=0RTtòÞË(¯ˆ€Å*D‚¢‘ÌE­Œ9êÖYK¿¶¥a¦‡Jû¾¾ØÇ¾?*íxO¾Cýb¸ö TÐÓtëàP+Ô.È×½2c_Ä9ˆmÝ
«^›â«óÊÕ‹T ‰Ö0Õ¦ÀÛVÁFìpDÂ'ÂC$ÑRU7,¡oØ!;» ¯Ç!å©T.éðÞ”3ÅÒ4°^.6Æâ¿ˆãPU1úÁô™mhjcDâÃ½IŽØ×ç_ü®	²à7Jáà êÐú±à5¯:WDÒÜRûkcÎ‘‘ò^úîˆ:ÖºãŠ¢ÑyOþä?Ðó^±,çSöâ£àÅKt‚Ü%jÁâ¡TZÿ3­üV11vu Jkägp=|¥œÛËIAúK(·¹[/Ÿ;GY¿C>€k.¼ŽD…ðN.î¼¢P^ûPüY…H%L;IÙÄçïßÿ	òƒôÎ„ßÑ‚ëÁÁ×ê¸ê ²U˜„@5&0n)ø‘Ùjâ/¿ÁY-Aô€hØ
v1jó‰˜µÀô‹='ãîÙ}WvûåÇˆÚo)èÛC¢þF^ùBª÷É
Šé—D•;{Ÿul Æd6r
Öå—6nÅšóÃô¡ä)sƒÐö!ÿœ>{Øa—¼,¥a=EîJ‘Û îþrÖj5Lôea;Æ-úÆköÕ.Ô½Q?	£þBvlË&IíCö…«Ì$ÂY?¼Øþkb-b€UÎ^—0ÉçR—BNg=½Ä!ü²à9{÷¦˜sÊ¥½vGìWó€tK!àC)ï 4
lÄŠf²t>à£E9ï6dRŽŸàZöùV8?aû´>6›û´,í0iÔµúÀ±7+³GÊüÄDdè!2H&qz‚S‘-ûê¯€¹:Ìf\? ôj‘¯ÙVÐŸž^P†õÅo¿¿<&nð;5‡_‡ËHc#ve&ÒŽÐÁïõŠ;®ý Þz$Â:ëø5Â6l=´£ia+¡‘+U6$˜SæW01ã	â:Å8nøû_h	å2WP8\/HÀ—O‘IþŠ¸ª»ümhw¿SÙ¦IÅèÎT}É‰j4Ú×2Û¶Û¡§ç!ÆçÇl‚À;õk,±ãjFû=¯)¿¥N?îÞPÆrš}šûˆWž-U­T0é¤à°)·¸pÉ ’‰„Cã_Eýq3©æ
mfgºÊSò%âÐØCGYðÜ"%Ô®}Ïv\¨è?3üè{E®ã¸€Õ!Ð­‡Lž€
pÚYå:ñîÎ;gk)Á	çK¾² w~AÐõ5xÐëëQhéUìû·?\ýòãÇ!Qc9Óy·eÐß“zHýGñx7ª‹™VØ˜è•Æ¢œ.ƒÿ·TXQÿÚY¨2ÂUf[%XSÂ8/“"½¼¶U4»HŠ©ag–}?bÓÉ›zT×~+\˜e—óååu˜á¨>3ˆwÚØØN‹¢–ôS¢à…ëÈí@ÚÛGrÚøU³ëV+þÀ&Ì~‘·^ÛcmºDeñÓ[Ó%£ µnçž¶+•„=§éÿ­!`"&T›úMRßâë½¡+xøÖ¹n¹Ú«ÜªÜcÃC6©š.ã2§~S\®!øN,¨Ñ9,põö+²*'E·”ÎåxW%Ëd.üjlLÚ¬Íà|‹o ñ Ø¶e]€b:YQŸ&¯¼÷>5ŽßC\ˆW¼Â}ï¶B¿B5_ÊDr&×¼Ñ“;«]5µ­oGßI|ãyÇ¥û¯P·OÈ¡`ñ}´/P“ƒ4FK? Ô­J™ Å x‰ûƒhÑwel?'»RZ…¾\ð“sÐ¤WA¡ÁCÃ%aÙ(’SèJ9_¡ïEãÈ°q¿W)€,_áÈ2Œ-£[rCÔ¸dßCíâÖ§èoÝT˜óe^<´cªõ³¯«©fèÉL-ìIÙËß‘G>šö—ç²ìæ½€[3Irl•Š1¨?M²òŸ@‘Žj¨ÉNbÖ ©³©Z]ûÐóvmAýüà¨UGN“%Ì…(qoP—+ªûãÕ[î|ÙÛŽÛ;èmœêÎ¨gÕã°*Òú&{"2Ü‡ñ=vsÀû¦“ƒÿiFá€?}÷œJ]¼‡toÆ±R30±3ê)öÝ@èÀ.ZLî>ÔXÖŸ&"}Ž·˜Bú(s‡ãøpõsˆHõ¦JÜ¬¯vg]åùmÒÂ6²ÒGõnüóAqôô!vEP*ºÑ–FOÆìB%üôžF:	–úÕßïÝ@z­|–†âf½VÇ†™bHÓšE(u¾ãsÌV,®ÔV¨¬Ò¼CÕÐsB;D°\¯ü>aìàšg }QL÷ÛŸ9‰ñ)Gºc=RrÕ<H€~b¤ï#7"Áv·°½Óq_»¼Õˆ½5F›æ9Üc2¦*]äÂBvÒÁc÷r©­ëÑ»;z<óÛãÞ{Ð¨{èÀðÖ3Æx)÷fw£ëq»¿É=l,ö›Ûiv;<!SÛ÷1³º¿aŸ‰õXÝÏ¼îUƒÁ!f¥O¯üxRVÕlì}²Qé=¯Mxö@ÝË¤î…SÀn &ØZ÷¥Ùã–f[Û†È¡8öUÚ£Viàx§:­âãl‡ã'™bá£Ônšá§a_·Œ;Ìø(ºP÷°µ¢;Ðàî[_Ü¹¸;ÔÔ¯ú:ïÈuÞÁv¶³(žÞƒ=ÇAl9“NØ’'âÌ
|w¶£Õóà«$¤ï áwly’ÀÄTJu§çlizÐ”W­ zPÿ¦Ä',×7ã¿52}óâ[êg~3Fá=_ø»ïjÕ…tô–ŒAU­ÃvµªÔï•éJ£çtÂk à†"qu¯´]oMi($ÚÛ÷Öóç¨ü9öÜ‰;”ô¼9*oHÇqfS"qwáÌ­LÙUwØF4ØÉ]öt92]tùÔÙ‘`w`Âöò±Y>SØ?ã¾´=/JO¤½¢| ôú·õg™yzæ_ ä}0®Žµ}mcøÒTÿ:ÖßÇÄ—'¤ pø÷XÎ¾f/Æ[q“ä••1mMÁ®=Ï÷’Pû;¾hÍÂqzG]ôF]ÞJÉ¢¿Q€¥½­Ê÷ÃÎDÞuDâ›•¥ÿ3‘Èõ †Õ¶–PÁÔ}¢ë,¼Ó+<²NØ’Ssw$*‡©9Yð@ulíö¯átþ­ÁÀbJŽèáø‰À¡¢>ì“¡ã‘¸‰†‰ú—|Æ@ÜöêÞ–ùƒÍ¿qËë{oygo+â€çZz7úY¸ÑÿìÙxîÚj    0707010005c80f000081b40000000000000000000000015ab2175400000e37000000660000000000000000000000000000004200000000root/usr/share/doc/opensvc/template.service.container.ovm.conf.gz ‹      í][Û6~Ï¯ N°¶;“m_¦M°³m
Û4$í>…‡–(›°$ª$eûã÷œCR¢<ãËdÆÓq*If,‰—ó}çF1ÏŸ?äçÙsö€l.Q¥å²zÈìºL-‹û4÷°£{XÙ=û­™ìó³ßŸ}Có}3~öÇ¾ë•ÒéE;„’.Œðƒ22‰ªø4MOuý0-þ¨¥í~à¹¡+•VKi¤*e9»è\IEÆëÜ¶£¶sÁŒÐK™ˆ0&Ë¹ÐÒò2ñæ‚gì›ž†ö:Ú]7•Hd&¸g&@`2!Ao&;>ËdF]52ese,vÈ¤a©Ì2¡EiY¦U±q#Þ4†ö¾¡»_ßñ8B'ÉÏª<
ç êRˆ”YÅ¦(Ä–¥Ô¶æ9+x2¡ßN¤°Lá¤æÉ×,Sj6Ê,“‰VÊN*nç=B¡ï+Q~øõ;Æg¨ ,åyÈ-%gOp	¾íj
Ød@8B©â° CEm,+•°+Mÿ 8—Á˜½…¹:*ð¼†Fy	÷FmùÛñŽ¬Îs„XÂ¢/ýX›CŒªu"£9ÐãKUYø#J¸°'³Z«Ì_› ÑHeÊ­0¾‹º”×ìl%ËT­Ì†–Cƒ•Ðœ 1kcE¨²¡æ„‹1 ‚n¢®eú§¢.<-ýx‹ÁÙýj#SÐxT®6ÎÐVÜÐÑ¢ SD¤W¿¾#P¾»M&öˆêõ(42=†páÑQy›dŒ2¯Ñ–)–h:Ó5’$í ËÝ7%¯zi·Ò¶\ÏÀ XÌ\Ù/“:#oÀÈ¤{PŠU+öˆñ©4–ÉÜûln?*ë‘h‘Àcr¶}+ZPè–ˆC‘PÙn,J5©´Ee'|ªô£X£õÌÍ•c¹f-¸æ¦üRèhS¥ «r3ðñ˜…çÃ&\I„Ö‰Æd$ˆ³pïàKV0çqÈçïÆ­Lˆ®¼¹ˆè"8f„ëÔ´Óð¨9YT¹(À§Qw(Š"M]Uð(Å›,ç`H•
öB˜ë—ãv2QCƒå1ÀÀ“vÈ›dø°“ñÄJ˜!8P˜#Œ[èQÔ€âJ6åF:ên¶$v l¡q5›$Uý™Å•w`m*€^©iv®rRÍ0TüÇü{‡ —y®VMÚ#†°ÞP	!( ”¯]^îzàP¼cöüÍ¸î$Í-¸#%#š—Ð\°³áùð~q6z…¸zœ Nüb;˜…(z0&ˆWé5hnJ®¢éè]ô=Â|gp·½à&fÍ÷ï†øg#K+fèô#øÿ%t)ò0XŸðƒ!ÇŒeÈVs™ÌágÈn€ðõùÙ«¯˜“?Øö²ãn šÑTÕÀ#!B'1dÑ×ZtÖéRAÃ‘9p¯‚©—6^® >ÐSA«hä•SÄÓ-ð	fX¹"AÑ#|8Ü¥_
†¡Ã8œ³èP
Ø÷õù>öýQ+Ë{òj_0Ãu… b®òŠðI(5s+JÁÍ´ª+\“ü"ŽALkV€\aŠOÊk¬ %ZÏ,o6xÛŠØ˜ý †H\sp†H¢ŸdY_³„ž0C6:«Ç!ä©Ë\Òbß3Å­)`½\Þ‹{r º˜ý`ú…L´245±¢æÅ¹I•ŽÙ×g_ü³Z×üF!¥¹ÀZ7¼çUçŽ¨5»RîÞ˜óDd¤¼k}·G(UL0oÐ*ïÉ¿“üZÞK–å|Æ^œ¡<‰FÐ‚¹D)ü
2÷3­×1÷>vy@'yËà~îùJ1·k'…Ö_!’7ãÚçÖRÔ"Ã×…³ÀQSÞ½“I†ž×äÊƒÅŸeQˆTÂ´s?”M]Œðþý»¨!7Hg±áŠÿ58~Ä'@dk?p€jL`Ü^p#3õÔ-d~ƒ³ðRïÞ
°ì|ÜÆ1kéç{8NÊÝ³û®ì6ò¿¢Cí·äôÍ!^ÿF\ùB–îÓ5$Ó/=‰j;zŸ::ÀpÊj9í˜áBÖhÄ†ñÃðW^A”17mçòÏè³‡fÅ«J–ÐXO‘»Rä6ˆ;ÁœµRõmÙ·ã=ñš}µµe¯ÔOB©ÿ†[³ÝbhÓ³¾p¯s*€D8kí†ë Ö2¶˜åì5	Ó|!Õd%älÞÓëAÂ?!
^°ßÃsN±´“î˜ýŠ`^ v%D	øPÈ{ }±¢™,]÷øÆhQÌ»Y ”å'¸–}¶ÎOXÇ>­ÍF?-K[UöfeâHY€˜Š-DÁ$nILy²ˆuÙeÌÕb4›àú¡š|Í¶‚fùìôœ2¤¨/~ûýå1q3¸€ßÉ9Ü:\F³K=•Vs„~+îa·Ú™C‡„Bg? lüÖC;*hÍo%4íÊ2ÆÝ'„9Õ Þq3s¾”Ð\'Çm×ÿ€ñ¥’.óR ‹ë	Øò2ÉÝ§C¡"ÉuC»øLm^$Á‹ÑW8ÓòKNT£Ñ¾nÙ¶	ˆ8=ñ06?f8ÞYé÷½²ãjFûœ“”ÛP¤wo(zå?9É>Í}ÄKÏVe*¨t’7Ø[\¸dPÉDÂ¿¾°…"|ì\–‰63sUç)YŠŠq‡¨ì¾ºÌ[n‘‚iß³ç3úÏŒúN›¸. µD4ë>’' <œf^[¤N¼»ó£5Á^J0ÂùŠ¯4Œ;¿ÐÐÕXÐ««±/ï-Ù÷o¸üå§C¢Æj®ònù ë“êIÝÇñxodsUb‘¢,Ê4nÊªÊÛC€á×ÎB•¶Ö0Û:ÁœÆy‘éÅ•©ÓØ¡™eRÌ4ö-üˆ•«oÂ¨®ÜV˜0Ã.«‹+?Ãq¸3ˆwZßØN‹¼–tS"ç…ëÈí@Úî£vÚ¸U³«V*î‹›0»EÞ°0¶GÛT…Ââ§·¦KJAkÜ,m×eâ÷ž¦Aþ·‡‰˜Pnê6I]‰®÷úªà‘ç/hç¦ævr_ÌrëjÙ´nb¸ŒËœrø›Íå
œ«Á„ÃWo1"«stKé\.€wuQ¹‚!ZI›µœ+÷õ$2 Û¶¨PL§ë“#êÓäâ¥³àÎ¦Æþ{ˆñ%¹'pWÇ]¢]¡œ/å<9“ÖˆèÈSM%Ç¾Ö6¡ï„
®½cÒÝ#Tíã£FHX\]írÄhéµëJ&@1H^âú Z4Â]C`Ù KÑI¯JUŽhË7¥º7¤A¡ÀBÃ-~Ù(j§Pui]Z„¶•#ÃÂ	|®.²|#ËÐ·ŒÇlÅ5Qã‚}¹‹Ýœ¢ëºU(?ç?¼xh7”)Èg_US`èÉ)LhìIéËW‘G6šö—²êÆ½€[3I2luc>M°òO‘Žh¨ÈNbÔ ¡³®ZóüèÐòvmAµý`¨ËN;M”°¢Â½AU­)ïWo¹uio;ng ·þ—=«ŸU‘Ôo²'"Ã}÷±›Î6üOÓ{üéÙ0*!y÷áÞœc¦¦abkpfàÔS¬»×U´Ü}X†Oã‘†.Æ[Î |”¹Åq|¸üÙ{¤°©ÒŒ„RI¬ÎºÌóÛZóÛÈJçÕ»þÏ5‚£7±*‚BÑå`nÁ?¼Çý‰2yœ6þz,u«®Þ»Ð×Z¹(+.Äõf®Ž'2Å¦4‹Pê<ãbÌ¶Y\©­P¹LóU}Í	í@Árµvû„±kÞtpI1õ·?r“SötÇzÙä²y‘ #üDKWG$®E‚ån~{§c¾vY«1{«µÒÍ{¸Ç¤u]ÙÈ„ùè¤ƒÇî%äJÛ£wwôxæ¶Ç¼<ö P÷Ð=€âmFŒñRîÔî.J×ãv•{Ø:XìW·Ó¬vxBªæ·%î£f¡¾aŸŠõXÝO½îUƒÁ!j¥N/ýxRZÕlì}²R©=ï’{<{ î¥R÷Â) °¨)‚T&}jö¸©ÙVà¶!r(Ž}–ö¨YÚ8Þ)OëÀß£ø8	ÛaÊøIªØCø(¹Ûjø)JØ§ÇMãS¾’ƒ.Ô=lGÍèT¸;ÁÖ'wGNîUµÃ³†>Ï;ržw°žíÃ,Š@'^‚§÷bÏq[Í¥¦â‰gW`9Zx’® p,„Û±åI" ]—e÷uzÎ‘¤MºpÙ6D/ê_Wø†åæfü·Z¦o^|KõÌo†ãñØŸó…¿»ªVUHK§dêjh,–«ÕÕ œ+ÓmÞÓñÇ @‡"±¡VÚl–¦4” í­{ëùsTþœ{îÄŠzÞ•7$ãƒ8³…)QswáÌ­L¹ÑV¨°h°“/ªêérdº¨ê©³%"ÁnÇ„åå½z¦l?½öó©KÛsPª#ÝËâÛ © ãß6ßeæéÈ ä^}Ð6øÚ¾²1<4ÕÇÚãû˜øò„ ÆÿW–Ñ×ìÁa+®“¼6r)† ­˜"ÍKKïó½$ÔþŽ­øžÎ¨‹NÔUþTª¨-:XS%:­ÊÕÃÎEîÏÀŠ*"Çìíµtÿ%BÔäæ¨þ ÚöÅJ˜ºotü™^þ•uú‚­8wGMå05+‹pÈ9–v»c8­;5XLÁ½?80”Å—}`2ô}ÔÜTÁDÝ!Ÿ1·ÝÛ2póÿŠ¸åøÞ[Îìm›8à½–ÞŒ~fôÿßþòàj   0707010005c810000081b40000000000000000000000015ab2175400000df6000000660000000000000000000000000000004200000000root/usr/share/doc/opensvc/template.service.container.srp.conf.gz ‹      í]mo7þž_A (”àdÕÎµ_Ü&8_›E_RœÛÞ‡¢©]J"´KnI®dîÇßÌÜåJ–%Ç–+õ$ ‰­Ý’ó<3œ!g™—/Ÿòóâ%{ÂŠË´r\*aúÌ-+Á¬©#îi{÷´º{ñ[3Ø—ç¿¿ø‚ÆûGüâö}&–mòË¶Š—.œ=áud3]ñQ!š–~65¶ÃŒø£–F´]ø†–®TFÏ¥•ZI5¹ì\ÉÅ˜×…k{í¦€¢0s™‰8 &ÕTé¸ÊB£…àcöŽMÏcƒÁã¯ÛJdr,3¸g"@a2#Ak6ƒ;®…crLM5:eSm6È¤e¹…Ê±±ÑåÊxÓ ä}Aw¿]ïñ8bGÉZí†Ÿ§ j%DÎœf#TbË\Wó‚•<›‚Òï'1Ø¦ø@Róä[6Öz6ÚÎ³¡ÑÚ+î¦'„Bßz4>TB]ÿúã4	€@*ëxQ rsÉYÅ³\‚o»–>NPª¸$ØPY[Ç”vvmèàAÇÒ°÷0VO^Ô ”+¸7‘nÇ;ÆuQ lÄP}úÚt”bum2a‰]ÈŸêÊÁ¡àÂ&žLja¶ÿßaˆF.sî„MÔJÞ²ÿ²…T¹^Ø+•0œ ±KëD©²bæ„KP1 ‚B7!«Å >òŒ^,¯"‡‡fÄóÜ°Úz§š ‘ª¯ÿõSk™¤hxè^Ÿˆ¶qÀÞðOQ4¹
=^×(i‹e!<ÃIÃQƒ÷ë¹2å0ÓF<‹Wiôùš¾xJ—¢Õ\˜D¸TNL„Yƒ@Õåô  Ò(t ¯s"Nz…‹¡²VFˆ²rC>ÒÆ(ç7;øæÊ¾<<ö<|ÓÎ^#­¡)µ?ÄiÝÁó½~J,dQ° rè“• ¢a–Ð
®‚Ñð4rwfº,¡g4¯Ã€¼¹ˆè"8`×<"‰vÜLD.Ê²*D	5!¥¦`ÄÖURØÂ
!J©sÁ^	{ûzÐ&Ô£>zèxI€p}Ö[%S/D/ŒgNÂÁ7Ã¡ßÂœ%²@ž°·ÒÇ(«’€Â€M®d2Ìªú/ž<€µ¹ zå¶ì'Àa5Áˆã7ìóï‚^…^4Ùô¢CÛø­Š¥Oï|k ªwÀ¾‚¿7x´¹…w¤¥˜Öpqñ%;ï_ôßàçgoÈ'yœ Nüb3˜¥(O`îLP¯6K°Üœr‚¦¡’ïæƒ‹¸m0´SþLÿC¼cpð0J±³!oGŽÁpŸ-¦2›ÂÏn àë‹ó7Ÿ1¯ðíª3Ý 4g#]k¬„'‰>ƒÄ°6¢³Ü“k :~hæ
º
®^º4ë¥6(^©jtòÞË(˜ó˜	2˜ zŒ*Á¢!Güp$¸ì5tÃÄ~øÉ¢C)`ßçÛØ÷G­?‘oWÿB	€GŠa:«TèLˆ">%\~a"º ‰Ñu…K[Ÿ¤1ˆmÝ
+.wð‘ÕEíâºH¢e1µ.ð®…•û‘¸åàô‘DßKUß²Œž°}vv^CÈS«B–ÒaÛ3¥Ò4°^Î×úât¼”™ÑV€æ6UA"N@rz“:°ÏÏ?ùÜdÁoÂÁ¢û¾à=o:w$ÒÜBû{SÎ‘‘ò^úý3êPkÌ”3º8‘ÿ^òïèy¯Ø¸àöêgÁ‹×è¸KÔ‚Å¯riýÏ´˜X;Ä<Ì±3ÈPZ#Œk¯s{99HÝ‡HÞÎ¬—Ï£¨—9 †¯KïQaz'—-/i*>–e)r	Ã.BGr6ò1Â‡?$‚|'½Ç†+áw´àØ9x,Î; >*[†1ÀÔ ¨¦ÆUjß3[üjÁ8Š %˜=`¶l»´ñDÊÚû2Ùp7÷‰Ýe·•ÿj¿§Ißî2ë¯Å•¯¤Jp-!™~HT»³ã³Ž@ÿÌFNÀºf¸5Z±âü0|¨¸EJ¥Ü ´ý”NŸ-ì°^UR°EJ‘» n(rÖj5L˜èËÖ¦í·ä‰·ì³ûP›ŸŒú ŒúoÙ¾-›$µ-‡è·ÌFH„£6¾{©ýGbÍS€YÎV—0*fRBN¦'z=‰Cø'DÁ3öíbÁ)–öÚ°_ÌK Ò-„P€…¼ Ð d°	+šÁÒõ€oŠÅ¼7p„uü×²Ï7Â¹Çm¿_LËÒƒFÕŽ½Y™…8R–à'FbŒbÁ$njŽx6KmÙg%ŒÕa4›áúß
"ß² 9>9¾IRÔW¿ýþzŸ¸Y\Àïä~nL°+3’Îp„~+îq#Ô»CDèBg?"lÃÖCÛ+¶¹RÁ†a€snÀ¼Óf$¦|.A\'Çmß~ñ¹–.s)€Ãõ‚|ù™äïHÓ¡XØâ›¡Ý|¦¶=/²8‹ÑW8Rõ)'ªQoß6Èl`f âø<ÄÓøü”M0ñNüKcì¸šÑ>ç5Eªõ?Þ¿©Œÿè4{˜ûˆWž-TT*˜tRpØ[\¸dPÉLÂ¿¡–¬…"~ÜTª™…D›Ù©®‹œ<EEq‡hì¡H)xn‘¢kß²2ú¿	þô½"Wq\Àj‰èÖC$O@8í´vHtwç[g£¿”à„‹_ZŒ;¿ èæ<èÍÍ T‰*öõûo®~ùþç>Qc1ÕE·
Í·Ie‰¾ƒ´¿kÙÅT+¬uó…ÊSQNWÁÿ[Ê ¬ˆ¿vªŒpµÑÖæ”ÐÏË¬Ì/ol§šgåÄ°3Ë¾„±8è]ìÕß
fÙålqyF8ˆ×aé®C;7¶Ã¢YKú!Ñä…ëÈmGÚæ9mGüªÙM«ÿÅ:Ì~‘7.Œm±6]¡²øñ­é’QÐZ·3OÛ¥ÊÂžÀa:äŸ4L˜ˆ	å¦~“Ô—xàzo¨Š þ‚u®Zn'÷Å,·®¶ØpŸê&†sYP¿.®Ð0øJL¨Ñ9Ìqõö+ÆuAŠn)]Èð®.«¤v+%í¸à|Õh qØ6E]€b>ZQ“‹WÞƒ{ŸšÎß}\ˆW45x…ûr`…~…r¾œ˜É™\ñv@DOîqtÕT¹d´¾	}'TðµÌ—î¡jŸ5BÂâë
h_ ’ƒ4FK? Ô-+™Å yIëƒhÑwe=¬h&»RZÑ–oŽîsšô*(5xh¸%,%rJ]+çÓ"ô½hc,œÀçjKìÙç–Á€-¸!j\²¯!wq«CôM·Æ|º—vmÍ˜¢~¶U5E†ÁDae/×¾"|4í/ÏdÕ{·fäØj•b?M°òï@‘Žj¨ÈNbÔ ¡³©3Z…à8' çí:Ú’JÄÁQ«Žœ&J˜	QáÞ ®–±·‰¯¸ã¡Ú3öÛ;èMœêŽèÄªçaU¢õuö$dxÒ6îç€÷MGÿaÎÂzö	œJLÞC¸7å˜©Ø&3˜Ôs¬»©«h1¸»ŽXÆO3#õ}Œ7Ÿ@ø(‡ý¸¾ú1ÌHqS¥é	¥’XuUwIÛÈJ?«wç?/G/´aU…¢kå`~!<¼eú*ã‡ú¶Ë‘³Ô¯øzïÂPkå£4¬¸·«¹:œÈK@šÒ,B©óŒ1[±¸R[[ ²Ê‹UCÍ	í@Á
½ôû„©ƒk^lðI1µ·=rÃcžéöõ*ÏUó"Fø™‘¾ŽHÜŠËÝÂöNÇ}Ýç­ì½1Ú4ïà“1uå¢“[ÞÒÖÐ{8z|ì·ÇŸ¼<¶½Õ%†'èžÀðV#Æt)÷f÷£;áöx“{
Ø:Xl7·ã¬v8 SÛ1³Xß°ÍÄNX=Î¼UƒÁ.f¥/ý8(«j6ö>Ú¨ô–wõž' eRÂ)"p?P#,­O©Ùó¦fÛ„È®8ž²´gÍÒvÀñAyZþŠÏ“°ífŒeŠ'Ÿ%wÛÑ?ÆO©Á~Ó¸ÝŒo‡ä õ	¶½ft;Üƒ`;%w{Nîv5µÝ³†Sž·ç<og;Û†YƒïÅžý ¶˜J'lÅ3qfž]åhñu<JBú À±~Ç–g™ LL­T÷uzÎz‰¦{MºpÕ
¢õo+|Ãru3þK#ów¯¾¤zæwýÁ`ÎùÂß}U«.¥£S2zuÕ·ËÕêªÏ•éJ£÷tÂ1 Ð È\¬•¶«¥)%€D[ëÞNüÙ+Ž„=âÅ'Þì•7¤ã8³)‰¸‡pæN¦¬ÉŠ¶	îå‹®NtÙ3]tuèlIHpÿÄ„ååC³x¦|Ì‘µÇV—¶å ÔðFzÐ;!:Â·rAÇ¿­¾ËÌó3€õÁ¸8Ô¶•á¡©þ8Ö¾Ï‰/ÏHà,ð?÷8ûœ½"lÅmVÔVÎE´5Wd¸rô>ßkBíïxÐš…ïéŒºäD]N¥JdÑÉÔ˜*ÑiU¾v*ŠpVR9`ïo¥?Y?¹z j8¨¶}±„¦î]gáL¯ðÊ:}ÁœŠ»QÍÉ2]Ž¥ÝþNçOSpD/Çvåðe}Ÿˆi¨?ä3â®£{[æ÷ÖÿË;Žï½ãÌÞVÄïµœÜè_ÂþŒJˆÅ'i    0707010005c811000081b40000000000000000000000015ab2175400000d86000000660000000000000000000000000000004300000000root/usr/share/doc/opensvc/template.service.container.vbox.conf.gz    ‹      í\ÿoÛ6ÿ½bp‹³½¤·ý’µÅå¶n[h·û¡Z¢lÂ©‘”î¿÷)‰²ãØiâ\ÜS€­‰-=‘ïóyßÈ'>þ?Ïž³üAq‰VŽK%Ì¹u)Ørª¯ï!îaG÷°º{ö©™ìó³?ž}Gó}C3~ö¿ë•6éE;Å_Œð•d]òi.š'}4>‡ñg%h‡ð#Ï-}S½”Vj%Õì¢óM*2^å®µ›f…YÊDÔ`RÍ…‘Ž«$<4<coÙ\ð´‘`0yaü÷¶‰Ìd×ÌhL&¤"xšMàŠÂ1™Ñ£¥²¹¶È¤e©Ì2a„r,3ºØ¸/ƒ¼ïèê7Û#ÞGý€“ƒäW­ŽÃÇ9¨Z	‘2§Ù•ØÀ²”ÆU<gOæ ô›Á‰,¶©¾¡©¹óË´Þ…¶Ëdb´v“’»yP@è'ÆûR¨¿ÏøM Ê:žç€ÜRrVòd_Á§]K§G(•Ü l¨¨¬cJ»vmèàÁ ç2³w0WOžW ”+¸6’.Ç+²*Ï6b	(‹>cmJ±º2‰°DŒ.ä@¯uéà?¡à‹]<™UÂ:mÿ¿	ÂT¦Ü	Q)yÍþÃVR¥ze7¬–Âp‚Æ®­EM•3'\‚Šº3¶éIi„(J7áSmÜÉ!Ò|s,Hp´ Ió0Ë¥0Ñ¦ZÃ£Ô¦Á×vèàþÁ°±ý•ÌsTc²Ô€Q`Õ
¾â±©‡+]022D˜ÓãÍ—øý‡ŽÙnšD;nf"öï²(sQ€EÓã hò¶*K¸•üË9ø”B§‚½öúå¸L$h¡>èpH€pC6Ø$Ó ¸Æ'a†à>`Ž0naF‘,P ù6åVz§²)	XìØAãr6IÊêó'w`m*€^©m'F€¾'å]Ä'ó‚^æ¹^5éŒÜ¹¥ü\ ”¯}>æŸÀ¡zÇì{ø?ã¦@šK8pGZ
B†+dìlx>|…œ^!®'€?Øf!ŠÌã	êÕf–›RoôKô9Â|gp·½à&vâ{ˆo‡ø†`#•3a:ðÿC%òz°!ÑG^Y‘Ùj.“9üùð >>?{õóúß®:á Mu¬±2
C™\eD§>K5?<b}®^º8M¥g`¤©èä½—S&ˆ	D#™‹Z%!rÔ?	îëÃ0õ8|°èP
Ø÷íù>öýYiÇ{òê_°€ÃüS#¨0 '"¯	ŸDÔ•Dí‚fFW%Ö¢_Å9ˆmÝ
«®OøÔê¼ru¡’¨ŽUÛoª„ÆìGpDâšc‚3Dý,UuÍºÃÙè¼‡”§R¹,¤ÃgSÎKÓÀz¹Ü‹¿Ç¡ªb
ôƒé21Ú
0ÐÔÆ*ˆÄ	ÈÆQoR§cöíÙWƒk‚,ø‹R8ø uhýXðšW+"in¥ýµ1ç‰ÈHy/ýöˆ:Ñº˜`UitÞ“ÿVòèy/Y–ó{q†Qðü%:Aîµ`ñ£TZÿ;Uÿ•CÌCŒ]@€Òù\Ï_)çörRþr™¼]X/Ÿ;GY¿C>€k.¼ŽD…ðN.ž¼¦P^ûPü]…H%L;IÙÔçïßÿ	òƒô¾	£×ƒƒÛê¸ê ²u˜„@5&0.+ù‘ÙjêØïpAK= Z¶‚Û|"f-0ý|ÇÉ¸{vß•ÝVþ[t¨ýŽ‚¾=$êoå•/¤ŠpŸ®¡˜~HT¹ÑûlÔ±?ÙÈX—À
ªF+6œ¦%·H©˜„¶ùgô³‡vÅËR*ÖSä®¹Éâ g­VCÀD_¶¶cÜ¢;Þ°onCmÙõ“0ê¿ dÇ¶l’Ô>9d_¸Æ=@"œµñÃ‹í¿&Ö2öXåìu	Ó|!õd%älÞÓëAÂß!^°ŸÞÃsN¹´×î˜ýŽ`^ n%„|(å=€Æ¡‚XÑL–¾øÆhQÎ»Y ”ã'¸–}¶ÎÏXÇ>­Í-K;Lu­>pìÍÊ,ä‘² ?1zˆ’IÜ…˜òdÛ²¯þ
˜«Ãl6ÁõB¯ù†íÍñÙée(Q_|úãå1q³¸€ß©9ü:\F³K3•Îp„þ®WÜqíñðîÐ#†ÐYÇ¯¶aë¡H[	\©2°a˜ Áœ0ï¸‚™Š9_J×)Æq[À?ÀøRK(—¹‚ÀázA¾|†LòWÄåP½íC»xOe˜^$u£p¦êkNT£Ñ¾iÙÁ6¬@Äéyˆ‡ñù1› ðÎüKcì¸šÑÞç5Eªõ¿ÞÞŒÿä4û4÷/!=[©Z©`ÒHÁaSnpá’=@%	ÿ†æŠúÇÍ¥ZX(´™ë*OÉS”ˆ;@c]Ás‹”P»ö=Ûq¡¢ÿÂHð¿@ß+rwÀ¬–n=dòT€ÓÎ+‡Ô‰ww~r¶ö—œp¾âk‚qç]]½º‡¶.Å~x÷ãåo?5VswÛFü3©Èqw«º˜k…Í)>±Pi,Êé2øK€õŸ…*#\e`¶U‚5%Œó")Ò‹+[¥q@³Ë¤˜6²ì5üŠKoëQ]ù­0pa–],VWa†ãú{˜A¼ëÐÆÆvZµ¤Ÿ/\GnÒ>>’ÓÄ¯š]µZñlÃìyë…±=Ö¦KT?½5]2
Z+àvái»VIØxšùŸ&bBµ©ß$õ-¸Þº"€G¿`›–Û©}±Ê­Ê=6<dÓªÉá2.sªá·Åå‚ïÁ‚ÃWo³"«rRtKé\.€wUQ²LæÂ¯ÆÆ¤ÍÚÎ·y’€mWÖ(¦ÓõÉõirñÒ{pïSãø=Ä…xE¡Á+Ü÷ï)ô+Tó¥\@$grÃÛ=¹³ÚUS«YÑú&pôTÁ7v\º¿…º}BÖ‹ï+ }š¤1Zú¡n]Ê(ÅKÜD‹F¸k,`"Ù•ÒjD[¾)ºÏhÒ« Ðà¡á’°lÉ)t¥œ/‹Ð÷¢qdØ8÷U
 Ë×8²cËxÌVÜ5.ØP»¸Í)úG·æ|†mË˜jýìëjªzrS{RöòÁwä‘¦ýå…,»y/àÖL’[¥bêŸ&YùW HG5Ôd'1k€ÔÙT	­‚…ÎMŒ	èy»Ž¶ žNpÔª#§ÉB”¸7¨Ë5Õýñê-w¾ìmÇíô.NugÔ³êqXi}›=îÃ€ø·sÀû¦“ƒÿiFá€?Ýû N¥.ÞCº7çX©˜Ø‚õûn t`-&wj,ëŸ&"}Ž·œAú(s‡ãøpùkˆHõ¦J3*%±;ë2Ïo’¶	•>ªwãŸŠ£7P°+‚RÑ­v0¿ŒnÞþ„JxÙ³ô,õ«¾ß»0ôZù,;.Äõf­Ž'2Å¦5‹PêÜãsÌV,®ÔV¨¬Ò¼CÕÐsB;D°\¯ý>aìàš÷`|QLÏÛŸ9‰É)Gºc½ˆtÙ¼H€~b¤ï#×"Áv·°½Óq_·y«1{gŒ6Í{¸ÇdLUºÈ……ì¤ƒÇíKÈ¥¶®GïîèñÌo?8xxìA î¡{ ÃÛÌã¥Ü;˜Ý]Œ®Çíþ&÷°u°Øon§Ùíð„L-lKÜÇÌêþ†}&Öcu?óº/T‡˜•>½òãIYU³±÷ÙF¥Ëƒlªê^&u/œjnjŠ •I_š=ni¶¸]ˆŠc_¥=j•v ŽwªÓ:ð÷(>NÁv˜1~–)ö>Jív ~Žö¥ÁqË¸ÃŒï€â uÛQ+ºîN°õÅÝ‘‹»CMíðª¡¯óŽ\çlgû0‹2ÐIÐàé½ØsÄVsé„-y"FVàÙØŽV¿ÎƒGIHß 8ÂïØò$€‰©”ê¾NÏÙ Òô ).[Aô¢þu‰oXnnÆ¿62}ûâ5õ3¿ŽÇãpÎþí»Zu!’1¨Ê¡uØ®V•ƒú\™®4zO' ‰«{¥ífkJC	 ÑÞ¾·ž?GåÏ‰°çNÜ¡< çÍQyC:>ˆ3;˜‰»gndÊ–¬ºÃ6¢Á­|ÑeO—#ÓE—O-	nLØ^>1«Ç`JûÜ—¶ç ÔðFzÐ;!:Å·RAÇ¿m¾ËÌÓ‘?@È¿ú`\?jûÚÆðÐTkïcâËR 8<Žô-{A0FØŠë$¯¬\Š!hk®Èpåè}¾—„Ú_ñ 5ŸÓuÑ‰º:œJÉ¢sª±T¢Óª|?ì\äá¬¨#rÌÞ]Kv$ró ÔpPmûb	LÝ7ºFáL¯ðÊ:}ÀVœš»#Q9LÍÉ‚ªck·?†ÓùSƒÅ”ÑËñSCE9|Ù&CŸGâ¦&êùŒ¸éèÞ–ùƒí3Âo8¾÷†3{[¼×Ò»Ñ/Âþ'.ÉŸÙd    0707010005c812000081b40000000000000000000000015ab2175400000e61000000660000000000000000000000000000004500000000root/usr/share/doc/opensvc/template.service.container.vcloud.conf.gz  ‹      í]mÛ6þž_A (œà¼în®ý²m‚ÛkS ¸¦9 iîCQxi‰²	K¤Ž¤ìõá~üÍ)‰²×/›]'ëœ´Ùµ¤9Ï3oäHûôéC~ž<eøAq‰VŽK%Ì¹U)Ø"Éu•~²¸‡ÝÃêîÉÍdŸžÿùäšïË0ã'Opøs±Zj“^¶£Xð²„gøA5ÙD—|’‹æNL%ð€ÿ®¤éÆÒè…´R+©¦tðž[:’ŠŒW¹ký›Vt@ª™0Òq•„ûä‚gì›	žÖƒæ+Œ?nK‘ÈL&pÎT€šdBZ;ØÎø0ì£çÇGi\ÅsvU–l¦­ƒA1‡‡ßŽà’Pk Ú·«¨¾-ªU¼_Tµï [œ¤f!QOàÁõü^8&3ºUÃXÒ2ÞIËR™eÂåXft±v"žD ÐÙ/7G¼Žú'ÉñèªVB¤Ìi6A%6°,ýžÌ@é·ƒ¹Ã¦ú‚¤æÊ—,Óz6Ú.’±ÑÚKîf=B¡7w¥Pï?þÄøM Ê:žç€ÜBrVòd‡àÛ®¥@Ä„#”Jn 	6TTÖ1¥]»6ôð`€sŒØk˜«§Ï+ÊœÉ
§ãY•ç[ÇQ†±6%†X]™DX"Fr Ç·ºtðŸPp`O¦•°NÛÿo‚0D#•)wÂ†[TJÞ°ÿ²¥T©^Ú B'hìÊ:QÔTY3sÂ%¨ A¡Û€°3:Ër<5ºú²ÄdMÛ>ƒðb²d¤!òˆh1`:èn5ÉAœ‚ÖAÚ_Óën¯iåúh¶©ùEAš2nÙ\é¥ª•?\pÎÉ€‹»áE‰ÓËXyíÃe»UßŒ¿xñÈRfÐ	±{Â‡ô­ÁÈãÝO™ê™`Hó–Pkz7t‡±L{<Ö­@¦$™œVä‰DüTŒð«ˆütöÓÒy­ÍÝ:Wz\!ŠÒùDwr.¨9r¬pŒ£…pÜÜT¾&ÁDk¸•ZOöê¬D†MÞ·”yÎ‚ÊaLV‚°â®…ô‰wŒÉŸ™èL/¥$&Ät†1'Äô"8bï5X‰vÜLEœÛKäIÙÝòM™£­Ê.¥“å¬º v±gÂÞ<µ“‰2ÔÇ “M ÜÖÉ4©&ã‰“0C .ÌÆ-ÌY$@¹$›p+}B¹.	HìØBãr:NÊê+Ë%ïÀÚT ½RÛ
NŒ }Ë)ºŠ?pÌvz™Ë²)µaÄÊ[ª!í”ò•¯ÅýÝ 8TïˆýÿgÕÄÅCs
¸¨\Z*@WPÄ\²óáÅð~q~öq8œøÅv0Qô`LP¯6+
!hoÍÞFß#ÌwqÛ.0`L¹qñnˆo	6R9ùWþ£D^6ùàÈ++Ò![Îd2ƒŸ¡6À×ç/¾óôÕ	7 ÍÙDWÀ+¡šÄ 1dPÅCÒÑY›K5?ÜbW/]¼DA÷ÀHRÑÉ{/?¢¼"«	ˆF2µJ0B4ä¨?Xg-ýÚ–†a˜z>Xt(ìûþbûþ]iÇ{òê_°€Ãµ Â`€ž¦[‡Z¡vA¾î•û&ÎAlëV€\õÚŸXW®^¤I´†©6Þ¶
6b¿€#>"‰~•ªºa	]a‡ìì¼‡”§R¹,¤Ã{SÎKÓÀz¹Ø‹¿Ç¡ªbôƒé21Ú
0ÐÔÆ*ˆÄ	†z“:±ïÏ¿ùœdÁo”ÂÁ¨CëÇ‚ç¼èœIsKíÏ9ODFÊ{é»#êXëbŒ+ŠFç=ùw’ÿ@Ï{Å²œOÙ³sŒ‚ÏÑ	:p—¨‹_¥ÒúŸiå·rˆyˆ±s¨PZ#?ƒóyà+åÜ^N
ÒŸC¹ÍíÜzùÜ9Êúò\sá=p$*„wrÉpç…òÚ‡âÏ²(D*aÚyHÊ&>Gx÷îm$ÈÒ{l8~G®—ÕqÔ'@e«0€jL`ÜRð#³ÕÄ/^þ€³Z‚èÑ
°ìbÔæ1ké{8NÆÝ³û®ìöËµ_SÐ·‡Dý¼ò™Tî“ÓÏ‰*wö.;ëØ ŒÈlä¬Ë/m$ÜŠ5ç‡éCÉ-R*æ¡íCþ9}ö°Ã.yYJÂzŠÜ•"·9@Üýå¬Õj˜èË6ÂvŒ[tÅKöÝ.Ô½Q?
£þBvlË&IíCö…«Ì$ÂY?¼Øþkb-b€UÎ^—0ÉçR—BNg=½Ä!ü²à9{ó¦˜sÊ¥½vGì#‚y	@º¥
ð¡”÷ …
6bE3Y:ðÑ¢œw²@)ÇOp-û|+œŸ°Ž}ZšÍ}Z–v˜4êZ}àØ›•YÈ#e~b"2ô$“¸=áÉ<¶e_ý0W‡Ùl‚ë„^-ò%Û
šãÓÓÊP¢>ûãÏçÇÄÍâ~§æðëpilÄ®ÌD:Ã:ø½^qÇµÄÃ»CDBg¿FØ†­‡vT -l%4r¥ÊÀ†a‚sjÀ¼ã
f"f|!A\§Çmÿã-¡\æ
J ‡ë	øò)2ÉŸ—Cu’¿í.à5•`z‘ÔQŒ¾Â™ªo9QFû²AfÛv;T âô<ÄÃøü˜Mx§~¥1v\Íh¯óšò[êôãîÅ`ü'§ÙÇ¹xéÙRÕJ“n@
›r+€—ì*™Hø74þµPÔ7“jn¡Ðfv¦«<%OQR î0 =t”Ï-Rb@íÚ÷lÇ…Šþ+#Á—@ß+rwÀ¬–n=dòT€ÓÎ*‡Ô‰wwÞ8[ûK	N8_ò•Á¸ó‚®¯Áƒ^_BK¯b?¿þåê÷_?‰Ë™Î»-ƒþžÔCêÇ8ŠÇ»Q]Ì´ÂÆDŸX¨4åtü¿¥
ÀŠú×ÎB•®20Û*ÁšÆy™éåµ­Ò8 ÙERL;³ìGø›N^Õ£ºö[aàÂ,»œ//¯ÃGõq˜A¼ëÐÆÆvZµ¤Ÿ/\GnÒÞ>’ÓÄ¯š]·Zñ_lÂìyë…±=Ö¦KT?½5]2
Z+àvîi»RIØxœùŸ&bBµ©ß$õ-¸Þº"€G¿`ë–Û©}±Ê­Ê=6<d“ªÉá2.sªá7Åå‚ïÁ‚ÃWo·"«rRtKé\ÎwUQ²LæÂ¯ÆÆ¤ÍÚÎ·ø’€m[Ö(¦“ÕÉõqrñÊ{pïSãø=Ä…xE¡Á+Ü÷n+ô+Tó¥\@$grÍÛ=¹³ÚUS›qÑú&pôTÁ7žw\º¿„º}BÖ‹ï+ }š¤1Zú¡nUÊ(ÅKÜD‹F¸k,`û9Ù•Òê,ôå‚Ÿœƒ&½

N	ËF‘œBWÊù²}/G†x]¥ ²|…#Ë0¶ŒFlÉQã’ýµ‹[Ÿ¢¿ukPaÎ—axñÐ6Œ©ÖÏ¾®¦š¡'g0µ°Ge/ï}GùhÚ_žË²›÷nÍ$É±U*Æ þ4ÉÊ¿E:ª¡&;‰Y¤Î¦Jh,tícL@ÏÛu´õóƒ£V9M–0¢Ä½A]®¨îWo¹óeo;nï ·qª;£žUŸ‡U‘Ö7Ù‘á>ˆï±›Þ7ü3
üéÚp*uñÒ½ÇJÍÀÄVÌ ¨§Øw¡»h1¹{_cYšˆ4ô9Þb
é£ÌŽãýÕo!"Õ›*q³¾VØu•ç·IÛÈJÕ»ñÏÅÑÓ‡ØA©èF;X=³/ü	•ðÓ{é$XêW|¿waèµòYv\ˆ›õZNdŠ- Mk¡Ô¹Æç˜­X\©­,PY¥y‡ª¡ç„v ˆ`¹^ù}ÂØÁ5Ï@ú¢˜î·?sãSŽtÇz¤äªy 3üÄHßG$nD‚ína{§ã¾vy«{mŒ6Ís¸ÇdLUºÈ……ì¤ƒÇî%äR[×£wwôxæ·Ç¼5<ö P÷Ð=€á­gŒñRîÌî.F×ãv“{Ø:Xì7·ÓìvxD¦¶%îcfuÃ>ë±ºŸyÝªƒCÌJŸ^ùñ¨¬ªÙØûd£Ò{^›ðìº—IÝ§Ý@M°1´2îK³Ï[šmn"‡âØWiŸµJ; Ç;Õiø{?OÁv˜1~’)ö~–Úí@3ü#ìKƒã–q‡ßÅAê¶£VtÜ`ë‹»#w‡šÚáUC_ç¹Î;ØÎöae ã ÁÓ{°ç8ˆ-gÒ	[òDœYï®Àv´úq|•„ô= €c!üŽ-O˜˜J©îãôœ"MšráªDêß”ø„åúfüF¦¯žýHýÌ¯†£Ñ(¼ç÷]­ºŽÞ’1¨Ê¡uØ®V•ƒú½2]iôœNx ÜP$®î•¶ë­)%€D{ûÞzþ•?'Âž;q‡ò€ž7Gåéø ÎlaJ$î.œ¹•)²êÛˆ;ù¢Ëž.G¦‹.;["ìLØ^>6ËÏÁ”ö¯¸/mÏ‹RÃéAï„èŸH½þmýYfžžùùGŒ«ãG@m_Û¾4Õ¿ŽµÇ÷sâËR 8ü;7gß³gc„­¸IòÊÊ…‚¶¦àŠWŽžç{N¨ý_´fá{zG]ôF]ÞJÉ¢¿Q€¥½­Ê÷ÃÎDÞuDâ›•¥ÿ3‘Èõ †Õ¶–PÁÔ}¢ë,¼Ó+<²N_°%§æîHTSs²àêØÚí_Ãéü[ƒÅ”ÑÃñCE9|Ø&CßGâ&&ê_òqÛ«{[æ6ÿ>Ä-¯ï½å½­ˆžkéÝèWáFÿ×r+W2l     0707010005c813000081b40000000000000000000000015ab2175400000ddd000000660000000000000000000000000000004100000000root/usr/share/doc/opensvc/template.service.container.vz.conf.gz  ‹      í]mÛ6þž_A (œàlw7×|Ù6Áíµ)Pô%$í}(
/-Q6a‰TIÊ^÷ãofHI”½^{³ë½uN’ìZÒˆœç™7r¤<þŸgÏÙ~P\¢•ãR	3dn]
¶üë^âðâVwÏ~o&ûüìg_Ó|ßÀŒŸ=Ã¡/Äz¥MzÑŽ@ñBÀÑ~PE6Ñ%Ÿæ¢¹ÓGSá}˜VÒˆvßóÜÒ‘Òè¥´R+©f#©Èx•»vÔn.˜f)QO€I5F:®’pÓ\ðŒ½esÁÓzD‚Áä…ñÇm)™ÉÎ™	Ð—LHEp7›À„c2£[5*esmÞIËR™eÂåXft±q"ž4y_ÓÙo¶G¼Žú'É/Z†sPµ"eN³)*±e)«xÎ
žÌAé7ƒÙkS}ARså–i½m—ÉÄhí&%wó¡€Ð÷¥P~û–ñš@ •u<Ï¹¥ä¬äÉÁ·]K—G(•Ü l¨¨¬cJ»vmèàÁ ç2³w0WOžW ”+87’NÇ3²*Ï6b	(‹¾cmJ±º2‰°DŒ.ä@/uéàPp`Of•°NÛÿo‚0D#•)wÂ†[TJ^³ÿ°•T©^Ù+¥0œ ±këDQSeÃÌ	— b …îË‡F©[@ÔmáPx4;…€fI¡¨–¡4"qÚ¬™Þˆs¨b¯¼Û=" Tæ€p¯âW&¯•Zë˜Š>¼ŒTÁ'µéB€„¸Ý\p«ê•ž”FÀ©nÂ§Ú¸“ó6Í‘c¹-¸›æ> í¥0Ñ¦ZÃ­Ôf0«cŒƒëÃ&®­dž³ r“• ÌèÀeiG!<ð8Œ…3]02
20!doâúƒŽÙ)‰vÜÌDœ»Hà„( ZÑíÀv5EF[•%\J1”åâe¡SÁ^{ýrÜN&4ÈP¦$@¸!l’iB)ã‰“0C -ÌÆ-Ì(’
 XÉ¦ÜJOÞMI@bÀ—³IRVŸY¬¼kSôJm+81ô=)gþ~Ç1ÿÑ!èežëUSJÀˆ!U±”CX”òµ¯5üÝ 8Tï˜}3n:ÉQs
îHK	–á
’´v6<¾Â/ÎF¯×€À‰_ì³EæñÀõb¼V`ähoÍ~Ž¾G˜ï.â¶\`ÀÄÎA|ñíßl è‹eV-ü?
£D^61àÈ++Ò![Íe2‡Ÿ!÷À×çg¯¾b^ÿàÛU'Ü 4£©®€5VB¶ŒAbÈ J©Œè¬=¤‚Žî±‚Ž‚«—..Áè©@*:yïåÇ”¥CDÀd"AÑHæ¢V	Fˆ†õ‡#Á}í®a¦‡Jû^ŸïcßŸ•v¼'ß¡þH1¬­4‚
ƒz"òšðI0×ií‚fFW%®³|ç ¶u+@®ºöæS«óÊÕE8H¢5µ-ð¦*Ì¾G$®9&8C$ÑORU×,¡+ìÎÁëqHy*•ËB:¼7åL±4¬—Ë­±øqª*¦@?˜~!£­ Mm¬‚Hœ€Jõ&u:f¯Ï¾øœdÁo”ÂÁ¨CëÇ‚ç¼êœIs+íÏ9ODFÊ{é·GÔ‰ÖÅ‹£óžü·’ÿ@Ï{É²œÏØ‹3Œ‚ç/Ñ	:p—¨‹_¥ÒúŸie«rˆyˆ±¨PZ#?ƒóyà+åÜ^N
Ò_!“·ëåsç(ëwÈpÍ…÷À‘¨ÞÉ%Ã×ÊkŠ?Ë¢©„iça )›úáýûŸ#A~ÞcÃ‘ð;Zp=8¸¬Ž; >*[‡9@h Tcã’©™­¦~qækœEÐDˆV€­`çã6ŸˆYL?ßÃq2îžÝwe·•‰µßQÐ·‡Dý­¼ò…TîÓ5Ó/‰*7zŸ:6 ã29ë¢e¨­Øp~˜>”Ü"¥bnÚ>äŸÑg;ìŠ—¥T ¬§È])r“ÄÝ-ÎZ­†€‰¾l+lÇ¸EW¼a_Ý†Ú²7ê'aÔCÈŽmÙ$©½sÈ¾pÿf*€D8kã‡ÛM¬eì°ÊÙë¦ùBêÉJÈÙ¼§×ƒ8„B¼`?¼‡)æœri¯Ý1ûÁ¼  ÝJøPÊ{ C±¢™,øÆhQÎ»sÃFXÇOp-ûl'œŸ°Ž}Z›ÍKZ–v˜4êZ}àØ›•YÈ#e~b*2ô$“¸Ã6åÉ"¶e_ý0W‡Ùl‚ë„^-òÛ	šã³ÓÊP¢¾øý—ÇÄÍâ~§æðëpilÌ.ÍT:Ã:ø½^qÇµÄÃ»CDBg¿FØ†­‡vT -l%4r¥ÊÀ†a‚sjÀ¼ã
f*æ|)A\§ÇmÿãK-¡\æ
J ‡ë	øò2ÉŸ—Cu—…¿í.à5•`z‘ÔQŒ¾Â™ª/¹ß÷ÂÑ¾iÙÁ6¬@Äéyˆ‡ñù1› ðÎüKcì¸šÑ^ç5Eªõ?Þ¾¡Œÿä4û4÷/!=[©Z©`ÒHÁaSnpá’=@%	ÿ†Æ¦ŠúãæR-,ÚÌÎu•§ä)J
Ä ±‡Ž™à¹EJ¨]ûží¸PÑf$ø_ ï¹‰;àVK„@·2y*Àiç•CêÄ»;?8[ûK	N8_ñµÁ¸ó‚®®Àƒ^]CË¢bß½ûþò×Ÿ>‰«¹Î»-QþžÔ#çÇ8ŽÇ»U]ÌµÂÆ+ŸX¨4åtü¿¥
ÀŠú×ÎB•®20Û*ÁšÆy‘éÅ•­Ò8 ÙeRÌYöüˆÝxoëQ]ù­0‹Ý‹ÕÅU˜á¸>3ˆwÚØØN‹¢–ôS¢à…ëÈí@ÚÛGrÚøU³«V+þ‹m˜ý"o½0¶ÇÚt‰Êâ§·¦KFAkÜ.<m×*	{OÓ!ÿKCÀDL¨6õ›¤¾Å×{CWð(ð¬sÓr;µ/V¹U¹Ç†‡lZ59\ÆeN5ü¶¸\C0ð XP£sXâêí¯VdUNŠn)Ëð®*J–É\øÕØ˜´Y›ÁùÆ@2 ã°íÊº Åtº>9¢>M.^zî}j¿‡¸¯(4x…ûÞT…~…j¾”ˆäLnx; ¢'wV»jj£2ZßŽ¾“*øÆÚŽK÷—P·OÈ¡`ñ}´/P“ƒ4FK? Ô­K™ Å x‰ûƒhÑwel¯%»RZhË7E÷¹ Mz<4œ–"9…®”óeú^4Ž'¨ÓNdùG–alÙŠ¢Æûj·9EëÖ Âœ/Âðâ¡mS­Ÿ}]M5COÎ`jaOÊ^>øŽ<òÑ´¿¼e7ïÜšI’c«TŒAýi’•ŠtTCMv³HM•Ð*XèJÆ˜€ž·ëhêWG­:rš,a!D‰{ƒºlzl›üŠ;_ö¶ãöz§º3êYõ8¬Š´¾Ížˆ÷a@|Û9à}ÓÉÁÿ4£pÀŸ®} §Rï!Ý›s¬ÔLlÁ‚zŠ}7:°‹“»5–õ§‰HCŸã-g>ÊÜá8>\þ"R½©ÒŒ„JIìÎºÌó›¤…md¥êÝøç‚âèé*ìŠ Tt«Ì/#„‹÷„?¡^ö,=Kýêï÷n ½V>KÃŽq½Y«cÃ‰L±¤iÍ"”:×ø³‹+µ•*«4ïP5ôœÐ,×k¿O;¸æñ_ÓýögNbrÊ‘îXO–\6`†ŸéûˆÄµH°Ý-lïtÜ×mÞjÌÞ£Móî1S•.ra!;éàqûr©­ëÑ»;z<óÛãÞ{Ð¨{èÀð63Æx)÷fw£ëq»¿É=l,ö›Ûiv;<!SÛ÷1³º¿aŸ‰õXÝÏ¼îUƒÁ!f¥O¯üxRVÕlì}²Qéò ›êº—IÝ§Ûšbc heÒ—f[šín"‡âØWiZ¥€ãê´ü=ŠS°fŒŸdŠ=„R»h†Ÿb„}ipÜ2î0ã; 8èBÝÃvÔŠî@ƒ»l}qwäâîPS;¼jèë¼#×yÛÙ>Ì¢t4xzö±Õ\:aKžˆ‘øî
lG«çÁWIHß 8ÂïØò$€‰©”ê>NÏÙ Òô ).[Aô þu‰OXnnÆcdúöÅ7ÔÏüv8Ã{¾ðwßÕªéè-ƒªZ‡íjU9¨ß+Ó•FÏé„× ÀEâê^i»ÙšÒPH´·ï­çÏQùs"ì¹w(èysTÞŽâÌ¦DâîÂ™™²%«î°hp+_tÙÓåÈtÑåSgKD‚Û¶—OÌê1˜²ûUµŸO_Úž¥†'ÒƒÞ	Ñ)>
zýÛæ³Ì<ùùGŒ«ãG@m_Û¾4Õ¿ŽµÇ÷1ñå	) œþG£×ìÁa+®“¼²r)† ­¸"Ã•£çù^jÇ­YøžÞQ½QW‡·RE²èìX*ÑÛª|?ì\äáXQGä˜½»–þ5ï‘ÈÍ †Õ¶–PÁÔ}¢kÞéY§/ØŠSsw$*‡©9Yð@ulíö¯átþ­ÁÀbJŽèáø©À¡¢>ì“¡ï#qSõ/ùŒ¸éÕ½-óÛï¿¿áõ½7¼³·qÀs-½ý,Üè†î:_³g     0707010005c814000081b40000000000000000000000015ab2175400000e0e000000660000000000000000000000000000004200000000root/usr/share/doc/opensvc/template.service.container.xen.conf.gz ‹      í][Û6~Ï¯ N°¶;“m^¦M°³m
½d¤Ý‡¢ðÐe–HU¤ìñbüžsHJ”={2ãÙq*If,‰—ó}çF1ÏŸ?äçÙsö€l.ÑÊr©D5dv]
v-Ô}š{ØÑ=¬ìžýÞLöùÙÏ¾¦ù¾Á?{†c_ˆõJWéE;ÅFøA™D—|š‹¦§Uý°JüYËJ´Cøžç†®”•^J#µ’jvÑ¹’ŠŒ×¹mGmç‚Q-e"Â˜TsQIËUâ;ÍÏØ[6<#&/*wÝ”"‘™Làž™ É„D½™îø ,“uÕÈ”Íµ±Ø!“†¥2ËD%”eY¥‹ñ¦1´÷5Ýýf{Ä;àœ$¿hu>ÎAÔJˆ”YÍ¦(Ä–¥¬lÍsVðdB¿œHa˜ÂHÍ“oX¦õ.l´Y&“Jk;)¹÷y„~ph¼/…úðÛ·ŒÏP% ©ŒåyÈ-%g%Op	¾íj
Ød@8B©ä aA‡ŠÚX¦´°ëŠþp.ƒ1{suTàyr÷FmùÛñŽ¬Îs„XÂ¢/ýX›CŒ®«D"Fr Ç—º´ðG(¸°‹'³Z«Í_› ÑHeÊ­0¾‹ZÉkö_¶’*Õ+³¡åÐ`)*NÐ˜µ±¢TÙPsÂÅ‹ ÁFwO"¸D#Ö-(F[H„hK4Gžñ …E(¡ŽÔ¨Iš%• Äº*J²¼ÝDÅË^Ú­´-¯f`ƒP,f®í—I‘-bdP¼Œƒ=RbÕŠ=²c©4–ÉÜ[%ln?:ë‘h‘Àcr–e'• À!‡"¡³Û±PzRVB¥ð©®Å=¨_h®Ë1àhÁ14ý€à—¢ŠF0ÕºR›aGˆ,<?6ÈJ"´Nä0&#A{ƒsÑ
®æ<8üÁ¢•ƒ	1À•7ñýAÇìƒ†`‘šv5'‹2ÄÔD¢šbS—%<JÑË9ÒB§‚½æúå¸LÔÐ Cy0ì¡„²Á&™>èa<±fAÌÆ-ªQÔ€¢6åF:ên¶$v ì q9›$eý™E5w`m*€^©iv®rRÎ0PùÇüG‡ —y®WMÒ#† ÒP ”¯]VèzàP¼cö-üÍxÕ	c›[8pG
…+® œ¾`gÃóá+üâlô
qõ8œøÅn0Qô`L¯®Ö ¹)¹Š¦£Ÿ£ïæ;ƒ‹¸í01sh¾‡øvˆop6RY1C§Áÿ£¨”ÈÃ`}º	†¢ætÈVs™ÌágÈR€ðõùÙ«¯˜“?ØvÕq7 Íhªk`‘× “2È'ëJtV‰RAÃ‘9p¯‚©—6N–©ôTÐ*ygåÇñ€GÀ´	<AÞB– ô9Â‡#ÁÝ*‹†aTaÎYt(ì{}¾}ÖÚòž|‡ÚÌ#Å0Ö*æ
	!¯	Ÿ„R3·žLÐ¬Òu‰+b_Ä1ˆiÍ
+¬’ð©ÑymÃr	´D«ij»Á›ÖcÆì{0Dâšc€3Dý$U}ÍzÂÙè¬‡§V¹,¤Å¾)fŠ[ÓÀz¹Ü‹{r º˜ý`ú…L*m(hjbDÍ	Šr“:³×g_ü³Z×üF!¥¹ÀZ7¼çUçŽ¨5»ÒîÞ˜óDd¤¼kýv:Ñº˜`ÞPé¼'ÿ­ä?Ðò^²,ç3öâ½àùK4‚Ì%JÁàW¹Ÿi²¶ˆ¹÷±È:É[÷sÏWŠ¹];)´þr‘¼Y×>·–¢~\¾.œŽšòîL2ô¼&Wl(þ,‹B¤¦û¤lêb„÷ïŽrƒt®øßQƒÃàà±àw@|D¶ös × ¨ÆÆÅm72SOÝ2Ú×8/%ðà­ [ÁÎÇm<³˜~¾‡ã¤Ü=»ïÊn#ÿ#:Ô~GNßâõ·âÊRE¸O×L¿ô$ªíè}6êè ÃE([Éh—À²F#6Œ†%7H©˜„¶sùgôÙÃ³âe)4ÖSä®¹É â>$g­T½ÃD[¶å¶cÜ¢'Þ°¯nCmÙ+õ“Pê¿!dÇÖl·Úôì£/Üi›
 ÎºrÃ‹õ?k[Ìröš„i¾z²r6ïéõ áŸ/ØïaŠ9§XÚIwÌ~C0/ H»B>òž@cŸÁF¬h&K×=¾1ZóîB(eù	®eŸí„óÖ±Aëc³ÍLËÒƒFÄ†½Y™…8R`'¦"C‘A0‰[Sž,b]vÙ_sµÍ&¸~@è…&ß° Y>;=§)ê‹ßÿxyLÜ.àwr·—‘ÄÆì²šJ[q„~+î¸öƒx8sèðCè¬ã„ßzhG­ù­„¦]©2ÐaÜ}B˜Ó
Ô;Î`¦bÎ—šë$ã¸-àú0¾ÔÒe® °¸^€-Ÿ!“Üq:êa\7´»€ÏÔf€áE¼}…3U_r¢öMƒÌ®=HÈ@ÄéYˆ‡±ù1›ÀñÎ”ß[ôÊŽ«ísNRnC‘~¼}CÑ+ÿÉIöiî#^Bx¶RA¨ ÒHÞ`Slpá’=@%	ÿú´Šð±s©mfæºÎS²%9âPÙ}m“·Ü"%Ó¾g;ÎgôŸ	þè;Anâ¸€Ö!Ð¬ûHž€òpšym‘:ñîÎÖ{)Áç+¾6Ð0îüBCWW`A¯®Æ¾¸T±ïÞ}ùëO‡DÕ\çÝâ5×'U3º1Žãñnes­°DÎ*›²ºôößP`Døµ³PU	[W0Û:ÁœÆy‘éÅ•©ÓØ¡™eRÌ*62ìøë&ß†Q]¹­00a†],VW~†ãpfï:´¾±y-é¦DÎ×‘Û´ÝGí´q«fW­TÜÛ0»EÞ°0¶GÛt‰Ââ§·¦KJAkÜ,m×*ñ{OÓ ÿKƒÃDL(7u›¤®Ä×{}UðÈó´sSs;¹/f¹u¹G‡‡lZ71\ÆeN9üvs¹gà*A0¡Fã°ÄÕÛ_ÈêœÝR:—à]]”®`ˆVccÒfmçŠM=É€ŒÀ¶+êÓéúäˆú4¹xé,¸³©±ÿâB¼"×àîªˆÚÊùR.À“3¹aí€ˆŽÜY0ÕTðêÛhmúN¨àJ ;&Ý=BÕ>>j„„ÅÕÐ¾@ IŒ–~ Q».eƒä%®¢E#Ü5–°šôJi5¢-ß”êÞ@’N…·øe£¨B×Êº´m/*G†…ø\­ ²|#ËÐ·ŒÇlÅ+¢Æûr»9E×u«P~Î~xñÐ¶”)Èg_US`èÉ)LhìIéËW‘G6šö—²ìÆ½€[3I2lµŠ1Ÿ&Xù·§HG4Td'1j€Ð¹ªZóõãèÐòvmA•å`¨U§&JXQâÞ .×”÷Ç«·Üº´··3Ð»8ÕQÏªÇaU$õmöDd¸â>nç€³M'ÿÓôÂzöŒJHÞ}¸7ç˜©U0±583pê)ÖÝ€ëÀ*Zî>,Ã§ñHCã-g>ÊÜâ8>\þâ=RØTiFB©$Vg]æùM­ùmd¥óê]ÿçÁÑ{pXA¡èV9˜[FðïqB%óÂÆ_¥nõÀÕ{7úZ+¥aÅ…¸ÞÌÕ±àD¦XÒ”fJg\ŒÙ6‹+µµ*«4ïPÕ×œÐ,×k·O¸æM—Sû#'19eOw¬—M.›	0ÂO*éêˆÄµH°ÜÍoïtÌ×mÖjÌÞU•®š÷p©ªêÒF&ÌG'<n_B.µ±=zwGgn{üÁÁÛÀcz uÝ(ÞfÄ/åÞAíî¢t=n÷W¹‡€­ƒÅ~u;Íj‡'¤j~[â>jêö©XÕýÔë¾P5¢VúôÒ'¥UÍÆÞ'+•Þó.¹Ç³ê^*u/œ·5ÅÂ@Ê¤OÍ75Û	Ü.DÅ±ÏÒ5K; Ç;åiø{'a;L?I{%w;P?E	ûÔà¸iÜaÊw@rÐ…º‡í¨Ý
w'ØúäîÈÉÝ¡ªvxÖÐçyGÎóÖ³}˜EèÄKðô^ì9b«¹´Â”<##ðì
,G¯óàQÒÕ  Ž…p;¶<I`RÕJu_§çlIzÐ¤—mCô¢þu‰oXnnÆSÉôí‹o¨žùíp<ûs¾ðwWÕªié”ŒA]Årµº„seº­Ñ{:þ èP$6ÔJ›ÍÒ”†@¢½uo=ŽÊŸaÏ¸Cq@Ï›£ò†d|gv0%jî.œ¹‘)[m…
Ûˆ·òE—=]ŽL]>u¶D$¸Ý1ayù¤Z=SvŸ^ûùÔ¥í9(Õ¿‘îåNˆNñm€TÐño›ï2ótär¯>T6øÚ¾²1<4ÕÇÚãû˜øò„ ÆÿOÑkö‚`Œ°×I^¹CÖLQÅ•¥÷ù^jÇƒÖ|OgÔE'êj*UÔ¬Œ©Våêaç"÷g`E‘cöîZºù£&7@õÕ¶/–PÂÔ}£käÏôò¯¬ÓlÅ©¸;j*‡©YY„CÎ±´ÛÃiÝ©ÁÀb
Žèåø©À¡ ,¾ì“¡ï£æ¦&êùŒ¸éèÞ–ùƒíÿ©à†ã{o8³·mâ€÷Zz3úY˜Ñÿ¯›`V^i    0707010005c815000081b40000000000000000000000015ab2175400000ef3000000660000000000000000000000000000004300000000root/usr/share/doc/opensvc/template.service.container.zone.conf.gz    ‹      í]moã6þ¾¿‚À¢ð.Îv“½î—´»¸\»}Ù¶í}(
‡–(›°$ª$eÇÅýø›’åÄ/ÙÄiÜÊÀ¶‰,Qä<Ïg†Cæùó‡ü<{ÎðƒÍ%ª´\–B™]W‚ý¡Jqæ¶w+»g¿6ƒ}~öÛ³/i¼ohÄÏžaçb½R:½hûŠ\X1QåÄXUÁ-£ü ¸L¢*>ÍEóÎŸt-ð-~¯¥mg¾å¹¡o*­–ÒHUÊrvÑù&¯sÛö¿ùF–s¡¥åeâß”ž±·l.xº!Œ]h÷½©D"3™À=3“	Þ–ð2•)·Â\´½eÿkß^
õ`ª¼ª$ñBÿL_½Ï˜–YÅ,<¼›{ÚÁÓ™œÕš[“Æ‹?e<³B3B1ªÖ‰ˆˆ†¸ŒÙOÐ‡ŽZV ¾ˆ±a¼dâºRÚ2•Ñ;ºÍgJãÕ¨©R\[hk‹Âëýãð“U.Ž}ÁnCc
þ£Y©`Lð¦”¥ºŠ›¢ë†n]£Ø˜åx„ÓW,Œ2c’^‘¨¢ÂÑæk6WÞ5ÝåÌÌ9ð¥Ò,Æ Ï/»Ü";¶0¹ä…89þ¢¨ŒÐK™ˆ0€‡¡rÄÆ@EÙÄ™B _H$”Y&4r+ÓªØ¸o"4èî77{¼Žð‚“ƒäG´–G€®NÁ¦‚,„‡e)µ­yªÌAè·ƒiKSx ©yò¨¿Ú†2Ëd¢•²“ŠÛyPk¶Q¨*Q~üåkon°µ–ç9 ·”œU<YÀWpµ«)hæ¥‰Pª¸$Ð®µ±`1m€³Ÿ8–Á˜½ƒ±:*ð&´§ÓÎ$à§¸#«óa#–€°è¢ïkÓQbˆ›J£9ÐãsUYø'JøbOfµ ËkþÞ¹Å)¨KyNÁJ–©Z™-‡+S0@cÖ0Ï*jN¸x Øè6 ’M”–3Y>"xï I@ñ*Þ"‰ç6¾z¡Ï$žÑÐ¿`™Ì½flÊv·éDÊE#¶Ë?HùÏ?¸ÆÉÄÂ2œ­´H¬ÒëÈ%uÒEQ;áí0èK•ƒ®õ"Þq­ó Ô #°8½‡@m8nâxóÀNÑ›’?Jx
bGÃÒæ]‹™+ûy’cPƒó/£IÔË:ØšR¬bº7]Ü°7ØÜ~TÖ#Ñ" ¶~+·ØûÝH€FíÄ¢T“JP;áSˆ•OÎzê’¦HÃÆë^I„Ö‰úd$æD Þt9•Ìyìdû;U˜¥@„–’7_âô³
$jÚixÔœÄE¾4½æ3E~»©+J” ‡ÏrÞ|¡RÁ^sýrÜ&jh¡<èêSÂÙ`“Lïè3žX¹¤ä%BÀEm…¤›r#u7[Ú—©f“¤ªÿbžüX›
 WjÚ†-@Þ“j†Îù¯Øçß:½ÌsµjÐc¤EîàôJùÚeBÜÛ 8ï˜}ÿe\wB·æÜ‘†Â?ÍK!/ØÙð|ø
/œ^!®'€/l³EæñÀñ¢KIÎq„åÑu„ùÎà"n{ÁL(ÚC¼â[&p„ÅŒ¢þï„.E:ëS,`Èk#Ò![Íe2‡Ÿ!2Àåó³W_¸\4(ôû²3Ý 4£©ª5FB,“Ä‰ÒÔZt2£©‚ á‡wÀ\Aß‚©—6NÑ;p¦‚VÑÈ;+ïsý	¥
`&H`6—%ˆgˆ†áÃ‘à.³èÒö¾n²èP
Ø÷ú|û~¯•å=ùµ/˜GŠÑâ‚êÖ{yEøàâJÈá4Óª®0üYìƒ˜Ö¬ ¹BfOÊkR„ÐeË›Þ–ƒ³oÁ‰kŽÎIô½,ëk–ÐfÈFç`õ8¸<u™ËBZ|7ùLqk
X/—7úâ¤¨.¦@?~!­Œ MM,‚¨9ÁAñ@nR¥cöúì³Á=¾-ø\8¸€24®/xÏ«ÎQkv¥Ü½1ç‰ÈHy×úîu¢T1Á¸A«¼'ÿNòhy/Y–ó{q†³àùK4‚Ì%JÁà%ÈÜÏ”w¯i!ÓÏ±ˆ:Á.grÏWò¹];)´þrž¼Y×>·–¼~‹| Ó\85å§w2É´~‰v8ØPüY…H%§µJêHÊ¦ÎGøðá‡¨!×Ig±áÿ;jpè<æŸ ‘­ý`j Tcã‚Žë™©§.uü%ŽÂK	f˜­ [ÁÎÇ­?³˜~¾‡ã¤Ü=»ïÊn#ÿj¿£Iß2ëßð+_P?|¦k¦_zÕvô!ut€aÊj9í¢TDFl?t*nR17m7åŸÑg;ÌŠW•,¡±ž"w¥Èm×Þ9k¥ê'L_šÑ¶cÜ¢'Þ°/v¡¶ì•úI(õ?²ck¶K†6oöÞ®.OG­]÷býÄZÆ£œ½&aš/¤š¬„œÍ{z=ˆAø7xÁöþ1çäK;éŽÙ/æ iWB”€¹¼ç ÐØG°+šÁÒ÷ß-òy·.b
*;9DÏ¶Âù	yìCÐj«î(-mÑiTA|Q¹?R`'¦"C‘3‰KSž,b]vÑ_cµèÍ&˜? ôB“oØVÐ,ŸÞ¤!ê‹_{yLÜ&ð;1‡ËÃe$±1»ÔSi5Gèà÷qÇÜâáÌ¡CÂw¡“Ç¿ôÐö
ZóK	M»²Ì@‡qõ	aN5¨wÁLV&@s`—ÜûŒ/•L±Ürešàè'`ËgÈ$wG…0÷Z]Àgj3@÷"	³]Â‘–Ÿs·Œ½}Ó ³m"qzâal~Ì&˜xg¥_[ôÊŽÙŒö9')· H?î^PôÊr’}šëˆ—àž­Ê TPé$o°É·¸0ePÉDÂÿ}ÙeEøØ¹,ë•Í\ÕyJ–¢¢‰¸Ã Tv_Ïç-·H‰Á´ïYŽóý_ŒúN›¸. µD4ëÞ“' <œf^[¤N¼ºóÞš`/%á|Å×T¯^vuôêjìªKöÍ»o/þþ'WÞ¿š«¼[°éÞI¼®ã¸¿7¢‹¹*±,Ô9e\OÅïÎþŠ Œ¿vUZØZÃhëcJèçER¤W¦Nã	Í,“b¦ÙÈ°¯àG¬~zuå–ÂV],VW~„ãð=Œ ^uhçÆvX4kI7$š¼0Üv¤}}ÔNÛ—5»j¥â.Ü„Ù%yCbl¶©
…ÅO/§KJA¹nŽ¶ë2ñkOÓ ÿGÁ„‰˜PlêI]‰æ{}UðÈó´sSs;±/F¹uµG‡‡lZ7>\ÆeN1üÍær“«Á€Ã³·?‘Õ9	º¥t.À»º¨\ÁeccÒf­ç
¬=É€ŒÀ¶ÍëÓéúäˆú4¹xé,¸³©ñü=t;“VNà®r¾D»B1_ÊEA´ºÖˆèÈSMEÞ¾Ö6¡ï¸
®ì¿cÒÝ#nÛ“ó!`qu´.ÈA£Ô4j×•L€b¼ÄõA”4ÂUC`Ù ‹ÿI¯JUŽ¢U I'‚B…†[|Ú(j§Pué7€¡íEåÈ°p‚ªOK€,_cÏ2œ[Æc¶âš¨qÁ¾ØÅnÑ½ºU(?æß½¸k7”)Èg_US`èÉ)LhìIéËGW‘G6šÖ—²êú½€[3H2luc>³ò_O‘Žh¨ÈN¢× ®³®Ê‚ù=8' åíÚ‚vS€¡.;í4^ÂBˆ
×UÕÔ7þ·.ìmûíô6NuGÔ³êqXIý&{"2Ü‡ñ;vsÀÙ¦“ƒÿiÎÂzöŒJÞ½»7ç©iØ&3˜ÔS¬»©«hÑ¹û°ŸfF:o9÷QæûññòG?#…E•¦'JbuÖežßÖš_&@VÆ›™›QRƒ 8Úû‰UäŠÞ(siÿðžéO”ÉãlØøû±Ôe\½w¡¯µr^V\àvún¬Ž'2Å¦4‹Pê<ã|Ì¶YÌÔÖ¨\¦y‡ª¾æ„V ˆ`¹Z»uÂØÀ5;\PLïÛï9‰É)ÏtÇÚlrÙl$@?ÑÒÕ‰k‘`¹›_Þé˜¯]ÖjÌÞi­t³ ×˜´®+™0ïtðØB®”±=zwGÏ²qð6ðØƒ@ÝC÷ Š·é1Æ©Ü;¨Ý]”®Çíþ*÷°u°Ø¯n§Yíð„TÍ/KÜGÍB}Ã>ë±ºŸzÝªƒCÔêO{RZÕ,ì}²R©={É=ž=P÷R©{áØÔA*“>4{ÜÐl+pÛ9Ç>J{Ô(í ï§uàïQ|œ€í0eü$Uì!|”Øí@5ü%ìCƒã†q‡)ßÁAê¶£Ft*Ü`ëƒ»#w‡ªÚáQCç9Î;XÏöay /ÁÓÛØsÄVsi…©x"FFàÙXŽ¶óàQÒÕ  Ž…p+¶<I`¢ë²ìn§çlIzÐ„—mCÌŽ;,7ã¿Ò2}ûâ+ªg~;Çþœ/üÝUµªBZ:%cPWCc±\­®á\™nk´OÇ /‰µÒf³4¥¡hoÝ[ÏŸ£òçDØs'îÐóæ¨¼!Ä™-L‰š»gneÊ¶B…mDƒ|QUO—#ÓEUO-	vOLX^>Ñ«Ç`Ê§œP~jui{Jõ;Ò½Ü	Ñ)îHÿ¶¹—™§#w€Ûú m˜?<jûÊÆðÐTwkïcâË ü“N£×ìÁa+®“¼6r)† ­˜"ÍKKûù^jÿÄƒÖ\§3ê¢u•?•*j‹VÆP‰N«rõ°s‘û3°¢ŠÈ1{w-Ý¡ˆšÜ< ÕTÛn,¡€©»£käÏôò[Öé[q*îŽšÊahVÜSK»Ý1œÖ,&çˆ6ÇOveq³þe"¼57U0PwÈgÄmG÷¶ÌÜüë·ß{Ë™½mìkéÍè_ÂŒþ?,È[o   0707010005c816000081b40000000000000000000000015ab2175400000dcb000000660000000000000000000000000000004000000000root/usr/share/doc/opensvc/template.service.disk.amazon.conf.gz   ‹      í\ko7ýž_A (œ`%­í&ýà&ÁzÛ¶mHÚýP65CI„f†³$G²Šýñ{.ŽlYrm9V!mdÍÌåãœû"ïðùó‡ü<{ÎðCâri¦=fµ`¼ä¨ê~âðq;wÏ~£Á>?þýÙ·n¼oÃˆŸ=£žOÅb®t~Öv`¦Š¦×úø¡I2™ªù°ËÆ>ëFÐ-þÛH-òkj­fÒHUÉjì.þÀã®äbÄ›Â¶ýþYUî‚¬&BKË«,´S>bïØDð<vB0YhÝÔ"“#™áž±¨ðlF·eªš	È/¤±nÆÐ´Éðû9›O¤¦æ™`FÔ\s+rwS£H«0›v^-X)Ê¡ÐtÕNDB÷LÆ+6$I–YÅ¸ï/Ø@ôvÆ‹Fô–ßÞ1÷Ï€}¨ JšDVÆ ñíÜ¡Å¹,
Æ‹Beè#dWbz†Kvâîó òTØ£Ê,&ÅX™Æ«`Õ™Ú#Z
‰mäA|"Kæö™ÚiêZiºÃHí»k8:Ã¾+“Þ4\xÁs“2'²S–i¶úa$™*KtðŒù‡è1©jÓc|ÆeÁ‡²vÑ ¸`*^›‰²½Dš·¨2½¨Ñð `ÐƒÎà[ÿäôëW¯¿q_¿yýêëÓ“u*TküöEh©'_JƒEù^Œd…IäíµÐ”•E×Ð3nÑEšI6RÚŽßU£31HPúÌ§Sk©Ð¿SÐPÏH™èb&
ÚË=
Ô®¶U*è÷¨¥¼¿×AîŸz‹~¨uðVê¢ÖB”µ½àC°tï^^y(c‰I–9TÑœµ½eÿkÛ¹fM‡J¡©ªÃ“£hüŽ,ž?ê±µ¨>ýú7_aÊÑ'#;¬qÆ‡' Ç;ƒAÀ(A5ž;«¼¼HÜ„à€}R° N´åz,lj¸Êº%¸êš3,W`€&ÊÛÁ‚Ã‚— {!ÌÕËA;˜DÐÑˆæãóéÛcG«d:ŠŽ vWÎœ)Å9©L?‘Õƒ94É1wUHìXg¥ÆYÝ|YGÿàvê¬Íè•›V°w'õ¢ÙoÔçß»Îmõ8Æ_‡²"”ŠE×‘2šÞûÿ'7— ×Þç#Í4¯Æ‚±ãÞIï”~8îŸ:‹äqœôÃz0á‹`îLL¯Òç=Lê”~J~wÑÌ]Á%Ü6‚\Dc_80úßàldeÅXèüÿºò±uÖ‡Ó†¼1"ïQdŸMð½A<» ŸOŽO_1?ÿ.èNÝ éUÖiç$zˆ#M#ÛÀ±\ døÑ|…»
S/m¾»6ÈSA*ù¹È"OÁÉBÄ)!±$GüP(>	Ai…nèØï,:”û^Ÿlbßeù|ÛÚ, å)Ttô$äU›Žá{È×œ	kÕÔLŽØWibZ³r!ÇãCƒ2<!‰‚Y]˜†GÈéJ¤nÀ~€!Wœœ‘èGY5W,sO yêŸÀêq„<MUÈÉ.Úv1S*Mõrv­/þAêGÕÄ¬·”™FvÍM:iZÇ¡x˜7©.¾>þê¸'ÈÂ_.„Ã4‡Æ÷…î9íÜ‘¦usåïM9ïˆL”÷Òo÷¨J•è°Õª8ÿVòoiyÏÙ¨àcöâ˜¼àÉK2‚æ’fÁÐO¹4þ;qçcãS‚" iKù”,òÀWs{99¤¿ì!’7Sãåsk]Ôo‰0Í¥·À‰¨àÞIFËçÊ£¥ï²,E.1ì"td¹6ññãO‰ ßIo±q%üM;‡Ç¢ßÁô!gE»~p@5%ðDÄž™fhHdÊoia–à=à­€­`'ƒ6žHY¦¯_¨ðw;å>°û®ì¦u¦µß;§o¶ñú×âÊ²Jp.L¿$jlÿã¨ßÑôdÖrí”áúu¿®ñ£ð¡æ†(•rÃ¡í]þ±ûl`‡™óº¦œCèygŠÜd ?:ÎÚY3.|uÜvŠ[òÄ[öê6Ôf¥~Jý7‚l×šíÕ—-‡è«AÚ9 Zûî¥ú‰5K-e9MÂ°˜Ju1r<9ÐëAÂ?OÙ‡bÁ],ígwÀ~%0Ï ¤Qòž  AÈ`V,ë®|S´\Ì»YPÊò=\Ë>^çlúÝ„–ËÄÇ”Ö¹eiKA£ŠÓÃ¾\™E)ióh(Fd!F&16äÙ4ÕeŸý•«¥h6£õ‡^ù–­Íòñþ9e¤¨/~ûýå.q3bu¯6nËÒŒØ¹J«9A‡¿ãŠ;­ýÞz$B:ëøa¶Ú^AZØJXÊ•Õ:\e>aÏ5Ô;Í`†bÂgâ:É8møöŸ)™ÓÖ+R Këlù˜˜äïHÓ!­Ê¤·»@Ï4æˆÂ‹,z1÷´ú;wTs½}»DfÛ(ûg!Ææ§l‚ãW]ÊdËžžó3å¦Ö½}C1(ÿÞÍìÓÜG<Gx6¯â¤Š¼)l[Qí×™FfÿÊQj‡;)¸¬¦‰63Õ¹³uÜWn…“²SH8Ñ„"‚hÚ7lÇ…Œþ/F‚/¾ŸÈUÜ´Ö‚ÌzˆäPN3i,Q'ÝÝù`M´—F¸˜ó…¡_œry	zy9ðI"¿ïßÿpþËŸ{Žó‰*:»~¡MŠ}¤ý½–]LúËB`Q¥…9Æª:Øã2 #âŸ…*-l£1Ú&£œý<ËÊüìÒ4yêÐÌ,+Çšõ{ƒ¯/Å»Ø«K¿fØÙt~vF8ˆ×1‚t×¡õí°œ×’~HÎyÑ:rÛ‘¶ùDNÛ¿jvÙÎŠÿá:Ì~‘7.ŒmÐ6UÓdñý[ÓuJáÖ
¸™zÚ.ª,ì	<Mƒüo‡I˜¸ÜÔo’úZïUàQà/´sUs;¹/e¹M½A‡{lØ,c¸—…Ëá¯‹+œ¯¡„šŒÃŒVo1bÔn¢[Jr
Þ5eÍF²~56%í¨à#É@Æ-`[uÅ|¸ådO“‹çÞ‚{›šúï-ÄWÎ5ø	weCÔio`YÎ<9“+ÖDôäESM¡y”ÑÚ&úN¨àK=;&Ý?âª}BÔˆ„Å×¸}H7cnéBí¢–(†ä%­r‹F´k–Õ#§W•ªúnË7wÐ˜I?¥‚…Æ-aÙ(‘Sª¦²>-"ÛKÊ1¢Â	z®© Y± žÈ·lÎµ£Æû¹‹]¢oºU¨0æ³Ð½´k×”)ÎÏ¦ª¦ÈÐ½S˜(ìIéË'_‘çl´Û_žÊº÷·å akªƒøY+ÿ	éL+²“5 tÖMæVÁ0LjîÈòvmÉ­S_ªŽœe”0¢¦½AU/BÁu_qËC­gì·7Ðë8ÕÑUÃªdÖ¯³'!Ã}¶q;¼mÚ;øŸ¦ø»gÀ¨Ää=„{N™šÆÀpfpê9ÕÝÀuP-wŸ"–ñ³ôH=ãÍÆea©ŸÎ)nª´oVP*IÕYçEq“´°M@¬ô^½ëÿ¼@Lœ"©TáBÑkå`~!<¼Áý‰*ãõ¥»`©_=ðõÞKC­•Ò¨âB\­æêTp"s*Y–f9”:Ïø³K+µ•«¼èP5¾C;Ž`…Zø}ÂÔÀ‘T.1>)vímŽœÄÅ>{º]½¿r¾|‘€"üLK_G$®DFåna{§c¾n³Vö^k¥—ïÐ“ÖMm¢“·/!×ÊØzwG¬Ð» oèêt x«cº”{µ»‹Òp»¿Ê=l,6«Û~V;<!UÛ÷Q³Xß°IÅXÝO½îÕƒmÔJí_úñ¤´j¹±÷§•JÕ[éÔ¨{©Ô½pŠÜÔ
1+‡ÔìqS³µÀ­Cd[YÚ£fi[àx§<­ÿÅÇIØ¶SÆ?¥Š%wÛRÿŒRƒÝ¦qÛ)ßÉAêl;Íè¶T¸;ÁvHîvœÜm«jÛg‡<oÇyÞÖz¶	³$½3¸/öì±ö”ÅþõSé(	ék €c)Âá‰Y&€‰nªªû:=gGÉL-Ó…óV{Qÿª¦7,W7ãßh™¿{ñÆÕ3¿ëƒpÎýí«ZU)­;%ã¨©{ÆR¹ZSÅseºÒÜ{:á 4(2k¥ÍjiÊ’ ÑÆº·vÊŸ=aÏ¸ãâ€ovÊ7Ç[qfSqwáÌL¹&+VØ&4¸•/ª>ÐeÇtQõSgKB‚Û•—_èùc0e	û_¸.mÃA©áô0ïÑ!½wüÛê»Ì<ïû„ü«ÚFÿPÛT6F‡¦úãXø>&¾<s cA‡W÷_³Æ[q•‘3ÑÃlaŠ4¯¬{Ÿï¥Cíëuç0«p*U"«ætÂ·
§UùzØ‰(ÂXIEä€½¿¢ÓÂ«q*rõ ÔpPmûb‰K˜ºotõÃ™^á•u÷›sWÜˆ*04+K¨N¥ÝþNëO‹]pä^Ž
êM”¥—}è iú=7T¨?ä3â¦£{[æÇC{S~ýøÞÎìmElñ^ËÁŒþ%Ìèÿ™{˜€c   0707010005c817000081b40000000000000000000000015ab2175400000dc2000000660000000000000000000000000000003e00000000root/usr/share/doc/opensvc/template.service.disk.disk.conf.gz ‹      í]ÿoã¶ÿýþ
‡"Ìö’Û]{K›Ã²ö
ëõÜµP	%Q6ITIÊŽ‡ýñ{ï‘”('Ž&ÎâNÚ:–ôD¾Ïç}#ŸÔçÏòóì9{ÀŠË¤¹1»¬}½¯¸ü€¸‡ÕÝ³Ÿq†Ï~yö%Í÷”füìŽûR,Jg'ÝíñØ¹ÌàØø?¨"“ªš'…hoöI7hñk#µèFñ-/©µšK#U%«éIïH&rÞ¶øª¢²š	--¯R£Bðœ½e3Á³0
Á`ÎB»ã¦©Ìe
çLE×¦¤¸ƒIáŒO3Á‹Š©œÙ™#ËNøÒë	´ùùë7Nóôˆ>þâÕ›WùÑ/ßdo’—ÇÇoøçë4mä¿ÅUs¸d·Zf,UÕ\èH¾ÓÉŠúñÇXÿÌªnÈqÊŽ_O×)›kÍ—ÿ×Ú^Q*)¤§ÉN¿¹V%éÕtÊ®¬¥Tc87;¾ÍsLµjêAÍ«j&­’n6i¼U"ºi7Ðµþ£Pƒ¦;M·£™Ó©z.S²ç¢`*ù—H­œ‹	œ-MÐ'“&
ß3U)˜"h8ÃLS×J[˜€—½W'ìÝû¯ÙOïÏþyH¾§P€Õ‡ÚÊ¼P¶­ZÃoO¯Çªßˆ\VÂ0ÞXm¤±¢²0tÄŽ[4ÐË•&SßU£S1‰ ûÄ/AN­¥‚ñ-™‚xBçzôÇýŒî—9¯2<'’R©LL åa<á\2JwÕ)ŒC­ƒ·Rçµ¢¬í9O€0{‡t{ä¡B;(YfÜ
sÒ–ý§»ÏµØŸ(·ªz<ù æ…ëF`h¢úøÓ×l!‹‚y•Ã˜Œì±f!íŒñàpfªÊá—Pg˜Vðö  Á	û¨JáD[®§ÂFâdY¢®ÒíË0Àoá\EÁe%œ†¹:œt“‰ä¨Ð§ ìˆ¬’é µÉˆ&èÁ@§ÎC¡ÉŒ#YÁ<XÂÁ’ˆ¹«’€Ä€u^jzžÖÙ;öÞê§îÀÚL ½2Ó	Nµ }Ÿ×SÍ~Æ1ÿÒ#èYQ¨Ej`Ä©0ùšÈ
Q*–Îµ^Õ;a_Ã¿!ÄÄ.¨;…wÀ"'4¯¦‚°£Ññè%þp4~IÉápâëÁ,E9€¹;0A½J/)z˜8(½~G˜ï.â¶\`À¹™øâÛ!¾!ØÈÊŠ©Ð=øÿ&tår,›ó"8òÆˆlÄ3™Îà{Ãàü||ôòsúß^õÂ@3NT¬1Ò6$FLT¦.DÙ	Ä :~¸Ä
:
®^Ú8+¥{`¤©èä}äê‹Ä	RˆF²A%!Zr„G‚	Ò
†¡Ã8\°èQ
Ø÷úxû~m”åù¶õ/T¦8¤Àø*è‰È+Â'øÝÎ:ä*B™³ÏâÄtnÈ³¥kxbTÑ€Ç_„I‡¬®ŒÓ#®y)¬Ðö-8"qÅ1Á!‰¾—UsÅRºÂŒØø¼‡”§©
¨x,Þ›r¦XšÖC©µ:w!Ž£jÊèÓ/eª•` ™‰U‰ô&U6a¯>ûœãeÁ_”ÂÁ¨CãÆ‚ç¼ìI³åÎ9ODFÊ;é·GÔs¥Ês°ÕªÈ+ù·ô¼g,/ø”½8Â(x|ˆNÐ‚»D-ü	êy÷¹ó¡±ˆ¹±—P ´V>‹Üó•rn''é‡#ÈäÍ¥qò¹µ”õ[ä¸æÒyàH”ïä’áÎK
åÁ‡âwY–"“0íÂ$c‰Ë>|x	rƒtŽø¿Ñ‚Ãàà²w@}P³Â}Ý 4 ª1g"ŒÌ4‰YB!S~‰³ðZ‚èÑ
°ìxÒå1kq­tÇÉ¸vß•Ý×°ßQÐ7ÛDýkyåYE¸'K(¦=‰;þ{6 ã2k9ëXáBÕhÄŠóÃô¡æ)sƒÐv!ßícl`‡YðºÆœ!õ¼3Enr€:Î:­ú€¾za;Æ-ºâ”½ºµù`ÔOÂ¨ÿ€íÚ²Ý¶Y{gŸ}5Pv&H„³Önx±ýbÍc€UÎF——R/„œÎz=ˆCø+dÁ—ì»0Å‚S.í´;a?!˜' ¤]Q>”ò@_ÁF¬h'KÇ=¾1Z”ó®C(eù®e­…ó¶¨oB‹*ñ)–u´,m5í¬zõcoWf!”%ø‰Däè!rH&qk)áéelË®ú+a®³ÙÔ†î ò”­Íòéþe(Q_üüËá.q3¸€ß«9Ü:\N›°3H«9B‡w\ûA<œ;tHø!ôÖñÂÆo=t£i~+¡•+«l&è6€5˜w\Á$bÆçÄõŠqÜp÷?`|®$”Ë¼‚ÀâzA
¾|ŠLrgÄåVetÚ]Àks€éE¢ý„3­þÈ‰j4ÚÓ™u›ÐPˆýóãóc6AàV½]JZÍè®sšrí*ôõöEoü{§Ù§¹xéÙ¢
J“nAò›r+€—ì*™Jø¯Ìc?Ü+Áeui Ðff¦š"#OQ‡}åN8;¦„‰Á@dÄ€àÚ7lÇùŠþwF‚ÿúN‘«¸.`µ¡å#dò”‡ÓÌ‹Ô‰ww¾³&øK	N¸X`£ˆ¸ó‚..Àƒ^\L\Q™ß7ï¾=ûñûO#¢Æb¦ŠÞ®Ÿ¿'
cœÄã½V]P‹
ó‰E•Å¢¬ª½ÿ7Tþì-Tia³mR¬)aœ'i™\˜&‹š™§åT³±a_Á×Š—âmÕ…Û
fØÉåâäÂÏpŽÃâ]‡.6vÓ¢¨%Ý”(xá:r7îö‘œn nÕì¢ÓŠûá:Ì&4UÑáÖ¦jTß¿5]2
Z+àæÒÑvY¥~Oài:ä¿+˜ˆ	Õ¦n“Ôµxàz¯ïŠ yþ‚u®Zn¯öÅ*·©7Øðˆ%M›Ãå\TÃ_W(®jts\½ýÑˆ¼)HÑ¥y	¼kÊšå²n56&mÞepÄÅ@2 ã°­Ëº Å,Ù¿v²§ÉÅ3çÁOã÷â+
NáÔ6„ƒv–e\@$ï÷ÂÅ@DGî<¸jLÍƒŒÎ7£ï¥
‰pUÊ*Î®ÛÇgP°¸¾ÚhûQc´ôBí²–)PŠ—¸?ˆp×Xv ¬ÎÉ®*UiË×µM‚&
JNñËF‘œR5•ueú^4Ž'ðº¦ÈŠ%Ž,ÇØ2™°×DöÔ.vuŠîÖAù9ŸøáÅC»fLA?›ºšC÷Î`‚°'e/]GùhÚ_¾”u?ïÜº&gtlMc>m²òO‘žj¨ÉNbÖ ©³n¨yb´Ð¾ç=oßÑ–Ü’	üXõä´YÂ¥5îªzž'hó+n¹ïõãvz§ú3Xõ8¬Š´~=îÃ€ø·sÀù¦½ƒÿiFa?]û N%ï>Ý›q¬Ô4Ll	Á‚z†}7:°‹“»Ëði#ÒÈåxó)¤²°8Žg?øˆ6Uâ§T…ÝYgEq“4¿M€¬tQ½ÿœ@PœB©ØA©èµv0·Œà/ÞþD•ò§úPÎž³Ô­¸~ïBßkå²4ì¸W«µ:6œÈ[@ÚÖ,B©wË1;±¸RÛ r•=ªúžÚ ‚jéö	c‡R¹}¸¢˜î·9sçûévõüÊYû fø©–®H\‰ÛÝüöNÏ}Ýæ­&ìÖJ·Ïà“ÖMm#æ³“·/!×ÊØ½»£Çs+ô.À[Ácz õ ÝÞjÆ/åÞÁìîbtn÷7¹‡€­‡ÅfsÛÏn‡'dj~[â>fú6™Ø€ÕýÌë¾PµlcVjÿÊ'eUíÆÞo6*UoeSP÷2©{á¸¨A+çCiö¸¥ÙZàÖ!²-ŽC•ö¨UÚ8Þ©NëÁ? ø8ÛvÆø›Lq€ðQj·-Íð·áPì¶ŒÛÎø¶(úP°í´¢ÛÒàîÛPÜí¸¸ÛÖÔ¶¯†:oÇuÞÖv¶	³(=÷Ü¿{vƒØb&­05OÅØ|w¶£…ÇyðUÒõ  Ž¥p;¶<M`¢›ªê?NÏÙA¤éƒ¶\8ëÑƒúW5>a¹ºÿ•–ÙÛ_Q?óÛÑd2ñïùÂ¿]W«*¥¥·d4õÈXlWkêƒð^™¾4zNÇ¿ n(Rz¥ÍjkJK	 ÑÆ¾·?;åÏž°çNÜ¡<`àÍNyC:ÞŠ3k˜‰»gndÊ5Y¡Ã6¢Á­|Qõ@—ÓEÕO-	nLØ^~®Á”ößq_Ú†¥ú'Ò½Þ	ÑŸÈ½þmõYfžÝ„Ü£Ú†øáQÛÔ6†/Mu¯cð}L|yJ
 gÿ§‡ñkö‚`Œ°WiÑ9#ÐÖ\‘æ•¥çù	µ?­{³òo¥ŠdÕÜÎ¨T¢·U¹~Ø™(ü;°¢ŽÈ	{w"±ç5¹úTÿ¢ÚîÁ*˜úOtý;½ü#ëô[pjîŽD05+Kî©Ž­Ýî5œÖ½5XLÉ=Ÿ*ÊâÃ>øiü=—(˜¨{ÉgÄM¯îí˜^Úûðë¯ï½á½ˆ-žkÜèïÂþ´Ÿi/g    0707010005c818000081b40000000000000000000000015ab2175400000d60000000660000000000000000000000000000003e00000000root/usr/share/doc/opensvc/template.service.disk.drbd.conf.gz ‹      í\]oã6}Ÿ_A`Pxk{’Ùö%í6ÛN¢³ÀL»E‘Pm–D•¤ìx±?~Ï%)‰r&±3‰³q¡ í$¶tEÞsîyÅçÏòçÙsö€?$.“f9fvS	–é$»¯¸ü¸‡ÕÝ³ßi²ÏOþxöµ›ï7ãgÏhÜK±Y+u×ÂàóÉþzLª*žä¢}Ð÷<7‚¾ÑâÏZjÑá£®Ý•V+i¤*e9?ëÝ’‰¯sÛúUº/d¹ZZ^¦áA¹à3ö–-ÏšQ†ù
í¿7•HåL¦¸f.JÜ›:­à	&Å‚•¼À-3f)Pªu*7F¥’[‘±µ´\!3B¯$¾l®šFäx_‰òÃoß2q…ÇZ'±–ªr&çµæ3f3™f}-3yERF¯„M_ÑP¦Ù+\@œâßÑÔwd6eú\žŒ:Ùk™çÌò%žÂ5M&’¦E•ËO-ç~Ý£e1}]`Ï¾†8Pg¦ÔMÌ©4>{î4¹F–!O€;ß‰™,¡2Þ]X	m¤±¢´„ˆâÁ†Ö VÝãCL›@Î`JRa|¦VÂ_¨6ÉÅJä41</ó(0^ftM$…€œËšñ4×:pý]·Â[ª‹JQTö‚'JÛ£CºýæA f0¦2“ìßœu£eÿížm«h‰RxTÙãÉ DX²¸‘Åý£që&œÙ•cLFöXã\ n®LUQü²ÕxF¾‹·_Òî?BpÊ>(87'Úr=6'‹*¸êgX¦À Œ£®*Üê}EÎeˆÅ^sõrÚM&öW3ÒÇúô„³Ñ6™F¤M–À©¦V®œOÅ9™Ì¤ç­‚KK8,É1w[Hì¸ÉKÍ/Òª~”÷x~ê¬Íè•™Npªô}QÍ!šýNcþ£GÐó<Wë6®aÄ©0†øšÈ’PÊ7Þµ^…Ô;eßâÿá&B¯»„ƒ;ð„Ä	ÍË¹`gìd|:~MœL^;äqœôÁÍ`¢À<˜P¯Ò›6hôsô9Á|gp	·à‚fÁ)'=bˆ?ldiÅ\èü?
]ú\ËV<GÄ‚#¯ÈÆl½é¿×<ðñéÉë/™×?|{Ù7€f’¨¬1ÒÖ.HŒ™(M.DÙ	âBŽÏ@¬pßÂÕKkbÏ°ð‰)I%'’ —Ð""`@	RD#ÊNƒJ(B´äh~8$¥†¡›qø`Ñ£Ø÷Õé.öýY+Ëòíë_°€sµŠÁ€žÚ•„Ê÷;Ñ¤qAs­êŠÉû"ÎALçV@.ÌÖÝÃ£ò2<!‰’èšÀ8=â…’zÊ¾‡#Wœœ1‘è'YÖW,uw˜1›œÂëq¤<u™ËBZz¶Ë™bi
¬—«kcñ7Ò8ÊºH@?L¿©VFÀ@3« '8z“*›²¯N¾ø®	²ð—KáðéÐø±Ð5¯{WDÒìZùkcÎ;"å½ôÛ#ê…RÅlµÊòßJþ==ï9›å|Î^œP<}INÐÂ]’}”Iã'î¼¯-abìu IkåS±È_]ÎíådþrŒLÞ,—Ï­uY¿%>À5ÞG¢Bxw.OÞ¸PÞøPú]…Èh‘#ÉXâs„÷ïŽùAzoÂßdÁÍàp[w >Ô¬x®ŸBP	¼ÍÈL˜
™âkšEÐ¢¢°ìtÚå1kÁôÓwÆ=°û®ì6ò?¢Gíw.è›}¢þµ¼òEoi+Ù ˜~HTÛÉûÙ¤g?È¬åÖ%¨ÂEÕh¶´,¥7D©˜mòOÜÏv˜5¯*ZÁRÏ;SäSð ã¬Ój˜ÍÂW/lÇ¸Ew¼a_Þ†Új0ê'aÔ#ÈmÙNR÷ä}Õ(;Ñ¬µ^lÿ±V±G *g§KHò¥Tk!ç‹^âþ‰,xÉ~x)æÜåÒ^»Söy íZˆø¸”÷ MC±¢¬û>à£årÞ[v»,?Âµì“áüŒuì}Ðr•øœÊ:¿›DI£jÔÇÞ®Ì"”üD"fä!fH&io)áé2¶e_ý˜«¥l6¥õƒf›É‰|ÃnÍòùñe”¨/~ÿãå!q3´€ß«9ü:ÜÌilÊÎu"­æþnVÜií‡ððîÐ#†Ð[Ço6aë¡¤…­„V®,g°aL0ì›Â¼ã
&¾’×+Æi[À?ÄøJI”Ë¼D	`i½ …/Ÿ“üq9¤U=Æí.Ð=µQz‘6QÌ}D3-_qG57Ú7-27°*q|âa|~Ì&ÞyÙßµ¦ÕŒî>¯)§ZÿëíŠÁøN³OsñéÙºl”*²ííŸ[.Z²T2u³Ø÷JpY.
mfªÎ3ç)ªf_9ê[€±SJ˜ˆ&ˆÌ1 qí;¶ãBEÿ#Áÿ}¯ÈmÜ¬Ö‚ÜzÈäPN³¨-Q'ÞÝùÁšÆ_J8á|Í7†ZEÁË./áA//§¾¨Cæ÷Ý»ïÏýéãØQc½Pyo×/<‚š1Nãñ^«.
ãe!±(³X”UUðÿÆU F4öª´°µÆlë”jJŒó,-²³KSgq@3«´˜k61ìüJm4o›Q]ú­0¸0ÃÎ–ë³Ë0Ãió=fï:t±±›–‹ZÒOÉ/ZGîÒ=>’ÓÄ¯š]vZñ\‡Ù/ò6c;¬MU¤,~|kºÎ(ÜZ7KOÛM™†=§éÿ¥0	W›úMRßâAë½¡+<
ü…un[n¯ö¥*·®vØð˜%u›ÃÍ¸Ì]]\®|'ÔäV´zû«³:wŠî(Ë%xW•ëó«±1ig]ç¸ØdÜ¶›². ˜%Ç×Nö4¹xî=¸÷©qüÓB|éBƒW¸k¢A{Ë2.ÉáÇúÞDôäž5®šRóFFç›àè{©B"|•²³ïö	Y#
ßWàör8¹¥µ›J¦ Š—¸?È-Ñ®!X6«gÎ®JUNÜ–oæÚ~¡I¯×àˆKÂ²Q$§Pui}YD¾—ŒcFt_]²|C#›Ql™NÙškG3öj»=EÿèÎ ÂœÏÂðâ¡]3¦F?»ºš†Á4Âž”½|ðyÎG»ýå¥¬úy/pk'é[]Æ4?m²òï@‘žj\“¤¬©³®S·
†iêÐsGž·ïh×©G]öä´YÂRˆŠöUµiš–ÛüŠ[z=›q{}§ú3Xõ8¬Š´~=îÃ€ø·sÀû¦£ƒÿiFá€¿»÷œJS¼‡toÁ©RÓ˜ØÁA=£¾„ê¢¥äîCƒeóÓF¤±ÏñVs¤2·4Žç¿„ˆÔlª´#q¥$ugçù§¤…mb¥êýøçBqŠ¤RW„KE¯µƒùe„póŽð'Ê”WKÁR¿zàû½[C¯•ÏÒ¨ãB\m×êÔp"3ji[³J½{|ŽÙ‰¥•ÚÚ€Êe–÷¨zNÜ„#X®6~Ÿ0vp$•KèÃÅîy»3'qqÌ‘îPï¯œ·/P†ŸjéûˆÄ•H©Ý-lïôÜ×mÞjÊÞi­tûí1i]W6ra!;éá±ã•#eì€ÞÝÑã3+ô!ÀÛÂc×câb€îo;cŒ—rï`vw1º·û›ÜCÀÖÃb·¹g·Ã2µ°-q3kúv™Ø€ÕýÌë¾PµìcVêøÊ'eUíÆÞg•ªö²©¨{™Ô½pj¸¨„¡•‹¡4{ÜÒìFànBd_‡*íQ«´=p¼SÖƒ@ñq
¶ýŒñ³Lq€ðQj·=ÍðsŒp([Æíg|{}¨ØZÑíipw‚m(î\ÜíkjûWCwà:oo;Û…Y”^ß‹=‡Al½V˜Š§bb]AíhÍë<t”„ô= À±~Ç–§© &º.Ëþëôœ"MÚrá¼ä^Ô¿ªèËíÍøo´ÌÞ¾øÆõ3¿O§ÓpÎýí»ZU!­;%cTWcc©]­®FÍ¹2}iî=p€?±é•6Û­)-%@¢}oÊŸ#aÏ¸ãò€7åÓñ^œ¹)‘¸»pæ“L¹&«é°hp+_T5ÐåÀtQÕSgKD‚Ûµ—_èõc0åæÓ”ÿ:}i;Jo¤½;Dz îø·íw™y6ñùW´mâG@mWÛšêcð}L|yê gAg™O¾b/Œ¶â*Ík#WbmÍáŠ4/­{Ÿï¥Cíï7Ã¬Â©T‘¬ŠÓ¹Þ*œVåûa"g`E‘Söî
"©ç5¹} j8¨¶{±ÄLý7º&áL¯ðÊºû€­¹kîŽDå˜š•T§Ön§õ§ƒÅ.9r/Ç'‚FŠ²ô² MŸGâ…‰úC>c >utoÇüæÐÞØ‡_?¾÷göv"öx¯ep£	7ú?1>>9b  0707010005c819000081b40000000000000000000000015ab2175400000f0e000000660000000000000000000000000000003d00000000root/usr/share/doc/opensvc/template.service.disk.gce.conf.gz  ‹      í]moã6þž_A`Qxgû’½d›Å¥íX´Ûmû¡(Z¢m6’¨’”÷ãofHJ”ÇÎ&NãVzëXÒˆœç™7r¤{ñâ1?G/Ø#~P\*ÍuŸÙe)Ø4÷ˆ÷¸º;ú'ûâø×£74ß3œñÑûZ,J§§ÍÝžGøAý˜D•|œ‰úVu%ð€¿WR‹tí@©Õ\©
YLéà·<3t$^e¶õª ²˜	--/ŸLð	{Ëf‚§a‚Á„…vÇM)9‘	œ3\›ài‰*æBGò3i,énmøýRXfgŽ:¨Ü ¶£7ôTlæÉÉ lÒ4\;úÃüï«ì:uz9zt
•yƒ…0öd0Þ¤T#ÿøsZëmM£á’'g¯ÓI¤é÷E*n§¨o<ÌÔ„¾#aÍ}„¯¥(.úÚö2…fÒ°Êˆ4ryVá\Ó*qÐ%*/¹• ,º´ÏŒbài¿ë³k9†åWð¿à¿ˆdñ"e<Ëp,R³)8è>+ûL°9×’Ö0®AnU–
F“‘#4À3öúxº‰8m-K`ü­¹Qà¼`ŠÂ3P²¸±Ïb=±‰Ò#ØXÀHY¢ZÏ=ƒ³Õ&åËœOÿÞ&ÙV;é†—e¶Ä/›ÔÌ~ž‰¬³3i";qØõo·_–WÆ‚,Æ-Ž¾sÃ2®á¶Ü™|À$ö„ªÜKDa•üûNPG ÏßDb;p¸è5½JŸrY€æ3™ÌÈ¥9Õj1ZÀ[HøuŒ?•Í½Yµ@äËRÿ¶1Ø©JC€4/ÍLu8#óŠaA1±ÐÒœeÅ‘î½Åˆfø„L2™€ã<6 ƒO0üq—îÍ8+X§·U6Ñ*wÖèÏÑªdÉJ¸¦*Q P,SÅ„×äp§Ð`ÒH"úƒH\ã>ÕÄ‘×´†ß¸„’'Nðïñî:ÂR£c¤OmÝaŸpQ “jVBÖ¥àb ‡Ê<Ï¹Ìp²Nî" DÒUÁzÓ$SUJiWeâ:Nbz.™ó3¥£qê†#ú”éÀÀlS®}p ÚÑ±Mx—~{¦X?}ó˜ÈÔÏ›K¡€A½½ÐsNyh­Î³0
 ¡#x>òkSj©`|K¦ wF
d"™˜ÌÚð~©C2h8'’R¨T!Ÿ„ñ„s	jwÕ\¡  	‘—vÄÇwÒõ‘Çª°@É2Åê´-û_sŸµl¬ÜªhWa –¼Šõ,\ßë×%…¯r“‘-Ö,¤µBT8\@Žðcâ"R¤Ê
ÿC‡ìRåÂ‰¶l$Næe&rà*ÝÎ°Tl(º\¼É0Šä@,öR˜›WÃf2‘ ÞõÑ}:Š¹Þ*™z¨M
N‰•sŠ¥0GŽ&3ˆdó`c–DÌ]•$v lòRÓQRVîÚÖ£û©{°6@¯Ô4‚]Ü•SÍ~Á1ÿÚÎ£²L-‚³ÁCÖj¯cY JX½ 7ª½
ªwÈ¾†ÿÅ2=N¢êS¸ñÑ²I;eÇý“þküáxðš<’Ã	àÄ6ƒ™‹¼s`‚z•^Rô0qPúýîVcî	.â¶\`ÀÈÌ@|ñÝßlda”-ø¿ºp¹åsžU"¬åõ}•Z™

Õ%þ|rüúsæôO…Ln šÁXUÀ#mÅ]á!
Sig'7€ …«¾ VÐQpõÒÆtŒT ¼O‚(i…ˆ€«R	p9Bf"¨#DMŽðáHpWæ)†ãpÁ¢E)`ß'ÛØ÷{¥,ïÈ·«¡Å‡?AÅ•û„W„OBÕ·[ñ
.hªUU29aŸÅ9ˆiÜ
fK×ð±QV9Ž¿ 	“ÐšÀ8=âšçPIë!û‘¸á˜àô‘DßË¢ºa	]õÔà¼‡”§*2™K‹÷¦œ)–¦€õr¾6w!Ž£¨ò1Ð¦ŸËD+#À@S« '8èMªtÈ¾8þì?¸TädÁ_”ÂÁ¨CãÆ‚ç¼n×såÎ9ODFÊ;éwGÔ‘RùlµÊ:òßIþ=ï9›d|Ê^c<y…NÐ‚»D-ü	jm÷¹sQQáïcì5Ô(­–Å"÷|¥œÛÉIAú«>dò¸ØCò¹µ”õãêäðUî<p$Ê‡wrÉpç%…òàCñ»Ìs‘J˜væ’²±Ë..>D‚Ü ýžÑÅÿ7Zp\â¨jV¸¯›ƒ[ÂŠ	<ad¦›%2ùZ™sZ‚èÑ
°ìdØä1ké·\ýÙdÜ»ïËîµ}ÄwôÍ.Q-¯|)ã¥ÌñŠéWžD•\L- …1«å¬K`…Kk©+ÎÓ‡’¤TÌBÛ…ücúla‡Yð²Äœ.õ¼7Ens€—´¦ÙhÕÌ°ðÕ
Û1nÑgìó»P›wFý,ŒúÙ¾-›$5wöÙ—ßá˜Ò¬µ^lÿXóØ#`•³Õ%Œ³k©F!§³Ž^â¾‚,øš½¿€)fœri§Ý!û	Á< í7×NŽ)å=€†¾‚XQO–Ž{|c´(çÝ„,PÊò\Ë>Þç#t
Ý†UâS,ëhYÚjêYðêÇ^¯ÌB)±÷m,&è!&Lâþä˜'­-'Wýå0W‹Ùl‚ë„^yÆ6‚fùôð‚2”¨/ùõÕ>q3Â®Ôa56dçz,­æüVÜÃ®¸s‡	?„Ö:~@Øø­‡fT Ío%Ôre1®÷°5˜w\ÁŒÅŒÏ%ˆkã¸-àîßc|®$”Ë¼€ÀâzA¾|ŠLrgÄåPØtw·¡Ý¼¦2=L/’Åè'œiñONT£ÑžÕÈlj¯€
Dž‡xŸ³	ï´híRÒjFsÓ”k ¯wo(zã?8Í>Ï}ÄsHÏEP*˜t’wØ”[\¸dPÉDÂ¿rûáV	.‹k…633Ue)yŠ2ì+7ÂÑØ1%tLè¹}Spí[¶ã|Eÿ#ÁŸ¾Sä*î€X-ÝºÏä	(§™U©ïî¼·&øK	N8[ð¥Á®Ý–]]½ºº¢2¿oÞ}{þã÷]ãÑb¦Z­*áž (Œqw­º˜)/ó‰E÷«Jïÿ]{²áÏÖB•¶ÒÔw…5%Œó4ÉÓÓ+S¥q@3ó$Ÿj60ìKøŠûoÃ¨®ÜV¸0ÃN¯§W~†Ãpfï:4±±™E-é¦DÁ×‘›4·ä4q«fWVÜë0›ÐªC‡·X[hö=8s#£ µn®m—Eâ÷ž§Cþ¯‚€‰˜Pmê6I]‹®÷ú®à‘ç/Xçªå¶j_¬r«r‹÷Ù¸ªs¸	—Õðëâ2ÁÀu‚`AÎaŽ«·?1©2RtCéL^ïª¼d™	·“vÒdpÄÅ@2 ã°mÊº°Ùl|xídÏ“‹çÎƒ;ŸÇï>.ÄœÂ©míŸÿH¹€HÎäŠ·":rO‚«ÆÔ<Èh|8úVª0®JYÅÙuûø¬
×WàZ[=9Hc´ôBí²”	PŠ—¸?ˆp×XÖÃ~\²«BÚòM]'ì©WA®ÀCOêÆØHN®ªÂº²}/Ç'ðºª È²%Žl‚±e8d®‰§ì¨]ìêÝ­ƒòs>õÃ‹‡¶fLA?ÛºšCÎ`‚°ge/—®#|4í/_Ë²÷nõ$É±UEŒAøÔÉÊÏž"-ÕP“öÃm!2$´
ÓÔ¾ç=oÛÑæÜ’	üX´äÔYÂµ%îªrš¿ëüŠ[î{=Ã¸ƒÞÄ©öŒ:V=«"­¯³'"ÃCßãn8ßtpð?Ï(ìñ§kÁ©„âÝ§{ø0
Ï4LléJI±ïBvÑbrw°Ÿ:"õ]Ž7ŸBú(3‹ã¸<ÿaå±‘z$TJbwÖy–Ý&Ío +]ToÇ?'§P*vEP*ºÖæëto	¢HxÙ±t,u«®ß»†Ð÷Z¹,;.ÄÍj­Ž'2Åº5‹Pj]ãrÌF,®ÔV¨\¤Y‹ª¾ç„v ˆ`™Zº}ÂØÁ¡T.A®(¦ûmÏœÄè#ÝþŸ`†Où"ÄH°ÝÍoï´Ü×]ÞjÈÞi­týî1i]•6ra>;iáq÷r©ŒíÐ»?zî)Æ=€·‚Çô4=ãÚA÷PÃ[Íã¥Ü{˜Ý}Œ®Ãíá&÷°µ°Ønn‡ÙíðŒLÍoK<ÄÌBÃ6ë°z˜y=ªƒ]ÌJ^ùñ¬¬ªÞØûd£RåN6Õõ “zN»cc heÔ•fO[šmn"»âØUiOZ¥í€ã½ê´üŠOS°ífŒŸdŠ„OR»íh†Ÿb„]i°ß2n7ãÛ¡8hCÝÁ¶×ŠnGƒ»l]q·çânWSÛ½jèê¼=×y;ÛÙ6Ì¢tä5xxöì±ÅLZaJžˆøî
lGóà«$¤ë sávly’ÀDWEÑ~œž³^¤é^].œ7‚èAý›Ÿ°\ÝŒÿRËôíË/©Ÿùm8ú÷|Yzu4½·%—–Þ’Ñ«Ê¾±Ø®V•½ð^™¶4½†n(z¥ÍjkJM	 ÑÖ¾·Ž?{åÏ°ç^Ü¡< ãÍ^yC:Þ‰3˜‰»gneÊš¬ÐaÑàN¾¨²£Ëžé¢ÊçÎ–ˆw&l/éÅS0¥†ý/Ü—¶åE©þ‰t¯wBtŒO¤‚^ÿ¶ú,3OîBîÑmCüð¨mkÃ—¦º×±vø>%¾<!€³ÀS¾`/	Æ[q“d•‘sÑmMÁi^Xzžï¡ö¯MïaVþ­T‘¬’Û•Jô¶*×;™VÔ9dïn@$ö¼F"W_€ê_TÛ<XBSû‰®§—d~`NÍÝ‘¨¦fe^¿Çž[î^ÃiÝ[ƒÅIýFz÷(Š²ø°¾@ÄLÔ½ä3â¶W÷6Ì/í}øúë{oygo#b‡çZ:7ú—p£ÿmYx
n    0707010005c81a000081b40000000000000000000000015ab2175400000d2f000000660000000000000000000000000000003e00000000root/usr/share/doc/opensvc/template.service.disk.loop.conf.gz ‹      í\moã6þ¾¿‚À¢ð.Îö%{í—´»¸\»Š¾ì»í}(Š„’h›°$ª$eÇ‡ûñ73$%ÊŽ_ÒØi\È@»Ž,Èyžy#Gzùò˜Ÿ/Ù?(.“f>dvU	–+U=VÜ? î¸º{ñ+NöåÅo/¾¤ù¾¥¿xãž‹ÕRéìª½ýDæ~ñƒú1©ªx’‹æNŸt÷aZü^K-²*­ÒHUÊrJ?~ËsC¿dbÂëÜ¶ƒþI•ôƒ,gBKËËÔß'|ÂÞ±™àY„`0_¡Ýï¦©œÈÎ™Š®MI+p“ÂŸfŽ	Oçpl!SÁð;ŒˆôÄ&už³ŠÛÙ®û’½e¥¶)×Èÿ>Så†6t{yqQM·Œ¥ª\Éw*YS:djÂ¬ÀiÛªvÜ¤p:í­âWŽý©*ohûý˜ÈRÆÛ+¡4V”†n„^pCDM‘5! ÇU­S1ŽÝ'>9•–
Æ·b
@¥sQ˜É(‘ãÄà~™Cñ2Ãs")¥ÊÄ(1	ã	ç¼îªUª›JQTö†'JÛ³CºùåXöJ–·Â\µ£eÿkï³a€‰Rp«²Ã“ï aÑà®Ù‡J”ùš-%z<§r“‘Ö,¥1ÎLUQ ü²ªñm›7?âô"8fU!œhËõTØHœ,ª\ÀUºa™À8êª‚K\Ò°œË
 {%ÌÝëq;™HÐ`‚ú€> a‡l°N¦j“%‚ñÔJ˜!æÈÑdF‘¬`À’ˆ¹ë’€Ä€m^jz“Vµ9;öîôS`m&€^™i§Z€¾oª)ˆf¿â˜ëô:ÏÕ28q*ŒA¾&²D”ò•óFWAõŽÙ×ðÆuì‚ÚS8p<!rBór*Ø»^ßà‹ÑòH'€l³EæéÀõ*½¢èaâ ôcta~0¸ˆÛ^p7fâ{ˆwC|O°‘¥S¡;ð/tér,[ð"8òÚˆlÈ–3™Îà{Ísà¾¼xó9súß^vÂ@3JT¬1ÒÖ$†L”¦.DÙ	Ä :~¸Ä
ú\½´&öxŒT ¼O‚(A…ˆ ÂHB4ÂäÔ«#DCŽðáHp !PZÁ0t‡Jû¾¸ÜÇ¾ßkeyO¾Cýæ‘ãR ¨0 §¦ªñIA~Gš4Õª®˜œ°ÏâÄ´nÈ³¥kxbT^ƒÇ_„IhC`œqÍa…³oÁ‰;Ž	ÎIôƒ,ë;–ÒfÈF—àõ8¤<u™ËBZ¼7åL±4¬—‹±¸qe]$@?˜~!S­Œ ÍL¬‚Hœà`x 7©²1ûââ³Â9^üE)@7<çMçŒHš]*wnÌy"2RÞIßQo”*n`ÀV«¼'ÿNòèy¯Ù$çSöê£àåkt‚Ü%jÁà¡L÷¹ó¡¶ˆ¹±s¨PZ#‹EîùJ9·““ô×CÈäÍÜ8ùÜZÊú-ò\sá<p$Ê‡wrÉpç…òàCñ»,
‘I˜vî’±Äå>ü	rƒt~ñ£‡ÁÁe!î€ú f…ûº9@h TcÏD™©³‚B¦øgáµÑ¢`+Øå¸Í'bÖâZÅŽ“q÷ì~(»7V‘ÞSÐ7‡Dý¼ò•,#Ü“Ó¯=‰j;ú0ul ÆdÖr
Ö%°Â…ªÑˆ5ç‡éCÅR*æ¡íBþ}ö°Ã,yUá
NŸz>˜"÷9À g­V}À_°ã]ñ–}¾µEoÔÏÂ¨ÿ†Ú²ÝÚusgŸ}ÕPv&H„³Önx±ýb-b€UÎ^—äs©n–BNg=½ŽâþYðœ}÷¦˜sÊ¥vÇìó
€´K!JÀ‡RÞK hì+ØˆÍdéwoŒå¼ÛJY~†kÙ[á<Â>Ñ}hQ%>Å²Ž–¥-&*¨{³2y¤,ÀO$b‚bÉ$îáá^^lË®ú+`®³ÙÔ†¦ ò-Û
šåÓóÊP¢¾úõ·×§ÄÍà~§æpëpÒØ˜]ëDZÍ:ø;¬¸ãÚâáÜ¡CÂ¡³Ž6~ë¡Hó[	\YNÀ†a‚s¦Á¼ã
&3¾ ®SŒã¶€»ÿ€ñ…’P.óJ ‹ë)øò)2É—CZÑmhw¯©Í Ó‹4D1:„3-ÿÎ‰j4Ú·2Û6’¡çç!Žãóc6Aà–]JZÍh¯sšrÛÅôu÷†¢7þ³ÓìóÜG¼†ôlY¥‚I7 y‡M¹À…Kö •L%ü+'±î”à²œ(´™™©:ÏÈSTa_¹ŽÆŽ)a"B01 ¸ö=Ûq¾¢ÿ‹‘àÏ@ß)rwÀ¬–nÝgò”‡ÓÌj‹Ô‰ww¾³&øK	N8_ò•Á¸ó‚noÁƒÞÞŽ]Q™ß7ï¿½þù‡OC¢Ær¦òÎ®Ÿ¿'
cÇãÝ¨.f
ÆË|bQf±(«*ïÿU F„?;UZØZÃlëkJçUZdW·¦Îâ€fi1ÕldØWðµä…xFuë¶ÂÀ…v5_^ÝúŽÃï0ƒx×¡í´(jI7%
^¸ŽÜ¤½}$§ˆ[5»mµâlÂìyÃÂØkS*‹Ÿßš.­p3w´]•©ßxžùß
&bBµ©Û$u-¸Þë»"€Gž¿`ë–Û©}±Ê­«=6<dIÝäp.sªá7Åå
‚ëÁ‚ÃWo6bRç¤è–Ò¹œïêÂµ†¹ÕØ˜´“6ƒ#.’€m[Ö(fÉùµ“=O.^;î|j¿‡¸_Rhp
§¶!´s°,ã"9ø±®·":rO‚«ÆÔ<Èh}8úNªW¥¬ãìº}|Ö‹ë+ }@Ò-ý€P»ªd
ƒâ%î¢E#Ü5–€Õ²«R•#ÚòÍ¨4éTP(ðÐpŠ_6Šäª.­+‹Ð÷¢qL°q¯«K€,_Qc*Æ–ñ˜-¹&j\±o v±ëSt·nÊÏùÊ/Ú†1ýìëj
=;ƒ	Âž•½|tyä£iy.«nÞ¸5“$ÇV—1áÓ$+ÿñé¨†šì$f:ë:¥U0˜¦ö=wèy»Ž¶à–Làç²#§ÉæBT¸7¨ªUhêmò+n¹ïõãvz§º3êYõ4¬Š´¾Éžˆa@|Ýp¾éìàžQØãO×Á©„âÝ§{3Ž•š†‰­ ˜APÏ°ïBvÑbr÷1`>MDºo1…ôQæÇññú'‘Â¦J3*%±;ë:Ïï“æ·	•.ªwãŸŠS(»"(ÝhsËþâ=áO”)¯z–ž‚¥nõÀõ{7ú^+—¥aÇ…¸[¯Õ±áDfØÒ´fJk\ŽÙŠÅ•ÚÚ •Ë,ïPÕ÷œÐ,W+·O;8”Ê%èÃÅt¿ý™“¸9çHwªçW®›	0ÃOµt}DâN¤Øîæ·w:îk—·³÷Z+Ý<G€{LZ×•\˜ÏN:xì^B®”±=zGO¬Ð§ o=èÔ=tG0¼õŒ1^Ê}€Ù=ÄèzÜorÇ€­ƒÅ~s;Ïn‡gdj~[â1fúö™XÕãÌë±P5bVêüÊgeUÍÆÞ6*UdS=P2©GáØT‚ •›¾4{ÚÒl+pÛ9Ç¾J{Ò*í T§uàïQ|š‚í0cüC¦ØCø$µÛføGŒ°/N[Æf|]¨{ØNZÑhp‚­/îN\Üjj‡W}wâ:ï`;Û‡Y”Þxžßƒ=§Al9“V˜Š§bd¾»ÛÑÂã<ø*	éz  ÇB¸[ž¦0ÑuYv§çlizÐ”×­ zPÿ®Â',×7ã¿Ò2{÷ê+êg~7Çþ=_ø·ëjU…´ô–ŒA]Åvµº„÷Êt¥Ñs:þ5 pC‘ÚÐ+mÖ[SJ ‰öö½õü9)Î„=âå=oNÊÒñAœÙÂ”HÜC8s/S6d…Ûˆ;ù¢ªž.'¦‹ªž;["ìLØ^~£—OÁ”ö¿p_Úž¥ú'Ò½Þ	ÑŸpï’6ëÏ2ólä^ ä}Ð6ÄÚ¾¶1|iª{kïSâËSR 8|ÁúèöŠ`Œ°wi^¹CÐÖ\‘æ¥¥çù^jÿØöfåßJÉÂ÷S©Do«rý°3‘ûw`E‘cöþDbÏk$rý¨þEµíƒ%T0uŸèùwzùGÖé [rjîŽDå05+î©Ž­Ýî5œÖ½5XLÉ=Ÿ*ÊâÃ>øi<‰KLÔ½ä3â¾W÷¶Ì/í}øæë{ïygo+â€çZz7ú—p£ÿ í¨æ¦b   0707010005c81b000081b40000000000000000000000015ab2175400000daa000000660000000000000000000000000000003c00000000root/usr/share/doc/opensvc/template.service.disk.lv.conf.gz   ‹      í]ïoÛ6þÞ¿‚@1¤ÅÙ¾¤·}ÉÚâr·ö0Ü¶—®÷aZ¢m"’¨‘”ßíþ÷{^’’(;Ž&ÎâAÚÚúñŠ|ŸçýERìóçùyöœ=à‡Ä¥Ò\˜]–‚eóû‹{ÀÄ=¬îžýL}~üË³¯]ß ÇÏžQ«¯Är¡tzÚ>¼à¹À‰á~H;&Q%g¢yÒG]Ñs˜¿VR‹tíD©Õ\©
YLÝÉ÷<3îL*&¼ÊlÛèTáNÈb&´´¼HÂs2Á'ì-›	žÖýÚŸ7¥HäD&¸f*
Ü›8­à	&ÁgÂéƒ©	³øž©©LxÆæ*«r1Âµ_»Ó¤Ï“M
OWu6Z{‚úôzdS­ª’Í”±hÚ&EÏ§Pó|ºQÍFþç÷åífE×·ìWÏŒ%ª˜É÷:Y€ÞLhfUÛø;ó—ŠëRC‡Ð‚ÈÑ½.Þ~ñß÷ÿz÷î·?ÿöéÿs0¹[Þ°“ã|P‰ÜŠUZˆ4=d+Í2qÝÁì,M%©
09œ¸qÿÞ€¢W0Dç9/ÒQÙ9ƒcÞ¹ÆŸ§ÌëÐëb‡C´ÐÊi¥*Ã–› -5Ž=Q$ßË}#&²†ñöÂRh#…EÓÐsN
&M²‰ÒDW•NDŒ×G~9¥–
í[2ª¸kI‚LÄ0s‘QÇð¼Ô£ààÅ5‘”B¥b'u{êkäþ®7h‡Ú˜¨¸ ‘—ö‚•¶‡tsæ¡¬J–),Åœ¶­e¿µÏY3ë±RxTÑáÉ·²;2â#‹ûìC)ŠóOg™e,¨m2²Ãš…´3Æ#€ë+ƒÅ£— ¬Nž7'é„ûCŽØ¹‚p¢-×Sa#q2/3‘ƒ«Ü{ThGU–¸ä’†eÜX–ƒXì…0×/Gmg"AGÒÇôé;`G«d:"m²1\Qb%zr¢œLfÉªÍƒ9,É§^+’@bÀ&/5½HÊê©FœÏôSw`m*@¯Ô´‚ƒ·/§Í~¦6ÿÒ<Y¦µ³¡'Â±,¥lé½QãUH½#öwüMÁ%B¯½„ƒ;ð„Ä	Í‹©`§ìxp2xEŽ‡¯œGò8N:°Ì\ä=˜ûêUzé¢‡‰ƒÒ÷Ñq—CÜ\Âm+¸`À…™A|ñíßldaÅTèüÿºð¹5–Íy†ˆG^‘Øb&“¾W<pøäøÕ—Ìë¾½è„@3«
¬1ÒV.H˜(L.DÙ	âBŽÏ@¬pgáê¥5±g gP¤‚Trò!	r•
"D‘ A4’™¨UB¢!GýáDp”Vh†®ÛáƒE‡R`ßW'ÛØ÷k¥,ïÉ·«!ÀR0~‚@Ec@Oí*KÂ'¡Â$T-µòÕ¿œ°/âÄ´näBoÝ=|lPã@†ç/$QÒ­	ŒÓ#®QîX¡Gì=‘¸æ”àˆDßÉ¢ºf‰»ÃØð^#å©ŠLæÒÒ³]ÎKS`½œ¯µÅßHí(ª|ú¡û¹L´2ššX‘8ÁaxÐ›Téˆ}uüÅ_qM…_.…ÃÒ¡ñm¡k^u®ˆ¤Ù…ò×ÆœwD&Ê{é·GÔ¥òªù´ÊzòßJþ=ï›d|Ê^S<yINÐÂ]’J¥ñß‰;*K˜‡{…:@Äc.T,òÀW—s{9)¤¿ “7WÆËçÖº¬ßàšsï#Q!¼;—Œ'/Ãp€÷¡ô]æ¹H%º…†¤lìs„¾ùFz3á7YpÝ8ÜVÇ¨5+žëû€Ð TcÏDÝ2SÍ…Lþ5õ"h	ÑÑ
Ø
v2jó‰˜µ4ú´…ãÎ¸{vß•ÝkÃ‰ï\Ð7»Dýµ¼ò…,"ÜÇKÓ/‰*;ü0vl í™µœÂºU¸¨Xq~”>Ð¸TsÃ¡íCþ±ûla‡Yð²¤œ>õ¼3Enr€ç€Ž³V«!`Ö_°ãÝñ†}yjóÞ¨Ÿ„Qÿ‰ Û·eûIŒæÉ!ûªPvŽi*‰z­}óbû¯‰5=U9[]Â8»’êb!ätÖÓëAÂß_±o? ‹w¹´×îˆ}"0O¤]Q —òž  Q¨`#V4uç¾1Z.çÝ„,(eùŽeo„ófŸnBËUâS*ëÜ°´¥¤QÕêƒcoFf‘GÊ~b,&ä!&H&i>wÌ“«Ø–}õ—£¯–²Ù„ÆzµÈ7l#h–O/(£D}ñó//÷‰›¡üNÍáÇá&Nc#v¦ÇÒjNÐáw=âNc?„‡w‡‰Ð„Î8~°	Sm« -L%4re1£ƒæTÃ¼ã
f,f|.!®SŒÓ´€þãs%Q.ó%€u™ðåSb’¿".‡´Ê£Ç¸Ùº§2G”^$us‡¨§ÅŸ¹£škí›™MK
PˆÃóãóc6!ðN‹Î,¥Íhïóšò Ü×Û'ƒñœfŸæ<âÒ³EQ+&Ý€¶Ë­ Ù*™Hü+'±î”à²¸2(´™™©*K§(ëyåV8;¥„cQ‘:Ô®}Ët\¨èÿ`$ø=Ð÷Š\Å¸Àj!È­‡LÞà4³ÊuâÙo­©ý¥„Î|i ˜f~!èòôòrä‹:d~ß¼{öÓwŽ‹™Ê:³~á™T·q·w­º˜)´—…Ä¢HcQV•ÁÿWQÿìTia+ÞV	Õ”hçi’§§—¦Jã€fæI>ÕlhØk|¥Å0oëV]ú©0¸0ÃN¯§—¡‡£ú<zÏ:´±±í–‹ZÒwÉ/GnÒ>>’Ó6Äš]¶ZñÖaöƒ¼õÀØkóKzøáé:£pcÜ\yÚ.‹$Ì	<M‡ü£BÀ$L\mê'Iýï«"À£À_Xçªåvj_ªr«r‹Ø¸jr¸	—™«á×Åe
ÁÀ¯¡‚šœÃœFo2bReNÑ-¥3yÞUyÉ&2~46&í¤Íàk’Œ;À¶)ëŠéøð–“=M.žyî}j¿4_¸Ðàî–Q£½ƒe)ˆäðc]o"zrOjWM©y-£õMpôTa,|•²Š³_í²F,~]›¨Éá4æ†~ Ô.K·ØÅK¼>ÈÑ¬!XvVOœ]ªº)ßÔ-÷‡&½
rKÂ°Q$'WUa}YD¾—ŒcB'è¾ª dÙ’Z6¡Ø2±×Ž§ìÔ.vµ‹þÑ­A…>Ÿ†æÅM[3¦Z?ÛV5Õ=8ƒ©…=){9÷+òœvóËW²ìæ½À­é¤slUcPšdåß"Õ¸Ev’²¤ÎºJÜ(º©Ãš;ò¼]G›sëLà§¢#§É®„(inP•Ëzuw“_qËÃZÏºÝÞAoâT·G=«‡U‘Ö×Ù‘á>ˆŸq;¼o:8øŸfø»{À©ÔÅ{H÷fÜ„…üK3õ”ÖÝ tÐ*ZJîÎk,ëO‘>Ç›O‘>ÊÌR;ÎÏ~©žTiZâJIZu–e7IÓÄJÕ»ñÏ„âI¥U.][æ‡ÂÍ[ÂŸ(^ö,ÝKýè_ïÝ@ÖZù,V\ˆëÕZœÈ”–€4K³J{|ŽÙŠ¥‘ÚÊ€ÊEšu¨Öœ¸G°L-ý<aìàH*—Ð‡/ŠÝó¶gNââ#Ý¾Þ_9k^$ ?ÑÒ¯#×"¡ånaz§ã¾nóV#öNk¥›÷hŽIëª´‘ÙIÛ‡KelÞÝÑã+ô>À[Ácz€º‡îo5cŒ‡rï`vw1º·û›ÜCÀÖÁb»¹æj‡'djaZâ>fV¯oØfb=V÷3¯ûBÕ`°‹Y©Ã+?ž”U5{ŸmTªÜÉ¦z îeR÷Â©Fàv Æ´0Z¹èK³Ç-Í6·	‘]qì«´G­ÒvÀñNuZþÅÇ)Øv3ÆÏ2ÅÂG©Ýv4ÃÏ1Â¾4Øo·›ñíPt¡îaÛkE·£ÁÝ	¶¾¸Ûsq·«©í^5ôuÞžë¼ílfQz4xx/öì±ÅLZaJžˆ¡´w-G«_ç¡­$d³	`.üŒ-OLtUÝ×é9;Š4}Ô”g­ ÷¢¾ßâqu2þµ–éÛ¯Ýzæ·ƒÑhöù¢ß~U«Ê¥u»dUåÀXZ®V•Gõ¾2]iî=° ([¯•6«KSJ€D[×½õüÙ+„=wâŽËzÞì•7NÇ;qfS"qwáÌLY“U¯°hp+_TÙÓeÏtQåSgKD‚Û-/¿Ð‹Ç`Jûx]Ú–RÃéAïÑ1½
·ýÛê»Ì<ú„ü«ÚÖñ# ¶mÙmšê·cíñ}L|yâ gAÿ‡Áð+öÂÁa+®“¬2r.ÐÖ®HóÂº÷ù^:Ôþ²ifv¥Šd•ÜÎ\©äv«òëag"{`E+"GìÝµô;×G"W7@Õ¶/–¸‚©ûF×0ìé^YwØ‚»ÅÝ‘¨]³2çê´´ÛoÃiý®Á`±KŽÜËñcA#EYzÙ‡6¦ã‘¸±BGý&Ÿ17mÝÛ2¿Þ´7öáëÛ÷Þ°go+b‡÷Zz7ú‡p£ÿ£wÓ	f    0707010005c81c000081b40000000000000000000000015ab2175400000dbb000000660000000000000000000000000000003d00000000root/usr/share/doc/opensvc/template.service.disk.lvm.conf.gz  ‹      í]mÛ6þž_A (œàlg7×~Ù6Áíµ).¸¶[ Iï€¢ðÒm–D•¤ìuq?þf†¤DÙë—Í®·ëBÚ®-iDÎóÌ9RŸ?ÈÏ³çì?(.•fÞgvU
–-ò{‹{Àˆ{XÝ=û'ûüì·g_Ó|ßàŒŸ=ÃaÏÅj©tzÑÜ½à¹€ƒü zL¢J>ÎD}§ºÂû0-~¯¤éÆR«…4R²˜ÒÁïyfèH*&¼Êl3èŸTAd1ZZ^$þ>™àö–ÍOÃ ƒù
íŽ›R$r"8g*
¸6!­ÀLg|œ	ÒSfáï…Ê*ø6Õª*áÄ¯éØ6Qj›6S3yeÖªÙÐf}äê<ˆ:Kx‘Ê”[a.ùìÍ U,„Ž0V
nU´°ø×ÏƒOÿeªÈVCÖµöX^ËÆ‚a™Ul‚â Í~øåGü¡‚¯… ÉƒÈ>s˜©,9 ™²W©X¼JÍüÕp8„Û,d"Ì]Á1½”ˆ>ãé`¦’øšH’»Z®]Ž´€Ñ+H‹[h¡JH˜?ÕÎ¶3#\²AŒcÙØbšh	z	èU¥*à`k„¤ápÞ¶MÃåâQ´»Ýð¶º±­Ú=¦Ë$Ø
x1d1©ÝY9[™ðÌû5s››#uƒ2w»¹RÃoO”Í6¾Y€:ysb)´DaaèFèG£&Éc¡ÖáwUéD#ó‘ÏAN©¥‚ñ­˜gFç¢p7ƒÜ[†ƒû¥ÞÏ‰¤*Cp²“0žp®—®Ú	o¡F¥"/íˆ•¶'‡t}äI…´÷“»z®ïõÙU)Š¿|Ë–2Ë˜W9ŒÉÈk–ÒÎ g&*Ï~Y ÕxŠöÌëƒx€þA‡ìƒ'Ñ–ë©°‘8™—™È«ÜyÙT`UYÂ¥@.iXÆÁ¡ä@,öB˜›—Ãf2‘ ÅäèÓ	¶Ïzëdê¡61ŽóÄJ˜!æÈÑdâÀÌƒ9X1w]Ø°ÍKMGIY=Õ¨û™~ê¬MÐ+5`uGåD³_qÌ¿µz™ejœŽ2ŠcY¤”‹9oT{Tï}ÿf\Ç.¨9…Œ4Ó¼˜
vÁÎúçý×øÃÙà5y$‡À‰?l3yæñÀõ*½¢èaâ ôcô;Â|gp·½àFfâ;ˆwC|K°‘…S¡[ðÿ[èÂå
8X¶àD,päg§}¶œÉdW<ÀÏçg¯¿dNÿàÛ‹V¸hcUkŒ´‰>…©€Qvq‚ŽsyàW/­‰=Þ#HE'ï“ ÊZ!"À€0$d&‚J0BÔäŽ¥Öpa.X´(ìûê|û~¯”åùõ/˜G
ŒH Â`€žˆ¼"|¬òBùá]ÕLNØqb·äÂÒ¯ácÅ	ÈpüI˜tÀ€6Æé×<Vè!û‘¸á˜àô‘D?È¢ºa	]aúlp^CÊS™Ì¥Å{SÎKSÀz¹Ø‹»ÇQTùèÓÏe¢•` ©‰U‰ô&U:d_}ñ8ÇË‚o”ÂÁ¨CãÆ‚ç¼nI³KåÎ9ODFÊ;é»#êH©|¶Zeùw’ÿ@Ï{É&Ÿ²gÏ_¢´à.QJ¥q#w®*ªÓ}ŒC€ÒjùX,rÏWÊ¹œ¤¿ìC&oæÆÉçÖRÖo‘àšsçã•0wrÉpç…òàCño™ç"•0íÌ$ec—#\]ý	rƒtŽøïhÁappYˆ; >\’[ù9@h TcÏD™©Æf…Lþ5ÎÂk	¢D+ÀV°óa“OÄ¬¦Ÿïá8wÇî»²ÛÈ?D‹Úï(è›C¢þF^ùBîãÓ/=‰*;¸šZ6 ã2k9ëXáBÕhÄšóÃô¡äÆ„eÂmòÏè³‡fÉËWpºÔóÎ¹Í~ è8k´êfXøj…í·èŠ7ìË]¨-:£~Fý7„ìØ–M’š;ûìËoMiÖÚ/¶ÿ@¬Eì°ÊÙëÆÙ\ªÑRÈé¬£×ƒ8„B<gï¯`Š§\ÚiwÈ~A0/ H»¢ |(å=€†¾‚XQO–Ž{|c´(çÝ†,PÊò\Ë>Û
çg¬c‚UâS,ëhYÚbÒ¨‚úÀ±×+³GJÜF‹	zˆ	$“06æÉ<¶eWýå0W‹Ùl‚ë„^ù†mÍòéée(Q_üúÛËcâfp¿Us„?ÔØ]ê±´š#tð=¬¸ãÚâáÜ¡CÂ¡µŽ6~ë¡Hó[	µ\YLÀ†a‚sªÁ¼ã
f,f|!A\«Çmwÿã%¡\æ” ×ðåSd’;#.‡´Ê£ÛÐî^S™¦IˆbôÎ´xÅ‰j4Ú752[Ø†ˆ8=ñ0>?fÞiÑÚ¥¤ÕŒæ:§)R­ûs÷†¢7þ“ÓìÓÜG¼„ôlY¥‚I× y‡M¹À…Kö •L$üWNb?Ü*Áe17Ph33SU–’§(Ã¾r#SÂ±Á@¤Ä€àÚ÷lÇùŠþ/F‚?}§ÈuÜ°Z"ºuŸÉPN3«,R'ÞÝyoMð—œp¶ä+‚qç]_ƒ½¾º¢2¿ïÞ}ùé‡}¢Ær¦²Ö®Ÿ¿'
cÆãÝ¨.f
ÆË|bQÄÝVÆªÒûC€ákk¡J[i˜m•`M	ã¼HòôâÚTiÐÌ"É§šûþÄ6¾·aT×n+\˜aóåÅµŸá0‡Ä»Mll¦EQKº)QðÂuäf Íí#9Í@ÜªÙu£÷Ã&Ì¾ÛÌ/Œí±6×2ÅOoM—Œ‚Ö
¸™;Ú®ŠÄï	<M‡ü³‚€‰˜Pmê6I]‹®÷ú®à‘ç/Xçºå¶j_¬r©n—÷Ù¸ªs¸	—Õð›â2ÁÀu‚`AÎa«·ŸŒ˜T)º¡t&çÀ»*/ÙDfÂ­ÆÆ¤4\Ô—Ç3ßþ¸¶mY ˜ŽO¯ìirñÒypçSãøÝÇ…ø‚BƒS8µá ƒe)ÉÁµ½Ñ‘{\5¦æAFã›ÀÑ·R×©ÛréîêöñY#,®¯€ö9Hc´ôBíª¤¦E(^âþ Z4Â]C`YX=!»*T1 -ß”þA“N¹§øe£HN®ªÂº²}/Ç'ðºª È²Žl‚±e8dK®‰ì;¨]ìúÝ­ƒòs¾ðÃ‹‡¶aLA?ûºšCOÎ`ždŸù‡¸›÷—ç²lç½€[=IrlUc>u²òO‘–j¨ÉNbÖ ©³®Zƒijßs‡ž·íhsnÉ>-9u–0¢Ä½AU®B#o_qË}¯g÷îþôöŒ:V=«"­o²'"Ã}ßc7œo:9øŸföøÓµàTBñîÓ½ÇJMÃÄVÌ ¨§Øw¡»h1¹û°Ÿ:"õ]Ž·˜Bú(3‹ãøpù“HaS¥	•’Øu™e·IóÛÈJÕÛñÏ	Å)”Š]”Šn´ƒ¹eñžð'Š„—KÁR·zàú½k}¯•ËÒ°ãBÜ¬×êØp"Sl©[³¥Ö5.ÇlÄâJme€ÊEšµ¨ê{Nh‚–©•Û'ŒJåôáŠbºßþÌIŒN9Òëù•ËúAÌð-]‘¸	¶»ùí–ûÚå­†ìÖJ×Ïà“ÖUi#æ³“{9RÆvèÝ=>±B¼5<ö=0&Ft`xëc¼”{³»‹Ñu¸Ýßä¶ûÍí4»ž©ùm‰û˜YèoØgbV÷3¯ûBUcpˆY©Ó+?ž”UÕ{ŸmTª<È¦: îeR÷Â) °¨16‚VF]iö¸¥ÙVà¶!r(Ž]•ö¨UÚ8Þ©NkÁß¡ø8ÛaÆøY¦ØAø(µÛfø9FØ•Ç-ã3¾Šƒ6ÔlG­è4¸;ÁÖwG.î5µÃ«†®Î;rw°íÃ,Ê@G^ƒ§÷`Ïq[Î¤¦ä‰ï®Àv´ð8¾JBÖ/IÌ…Û±åI" ]EûqzÎz‘¦{u¹pÙ¢õoJ|Âr}3þ-Ó·/¾¡~æ·ýápèßó…ß]W«Ê¥¥·dôª²o,¶«Ue/¼W¦-žÓñ¯€ŠÄ†^i³ÞšRSb×;þ<
N„=wâåoŽÊÒñAœÙÂ”HÜ]8s+S6d…Ûˆ;ù¢ÊŽ.G¦‹*Ÿ:["ìLØ^>ÒËÇ`ÊÃ¾úûiö¥íyQª"ÝëãÓ þÜëÏ2ótà^ ä}Ð6ÄÚ¾¶1|iª{k‡ïcâËR 8ü¿¾b/Æ[q“d•‘ÑmMÁi^Xzžï%¡ö÷mïaVþ­T‘,|S6•Jô¶*×;™VÔ9dïn@$ö¼F"×_€ê_TÛ<XBSû‰®§—d~`KNÍÝ‘¨¦feÎ=Õ±µÛ½†Óº·‹)9¢‡ãÇ†Š²ø°¾@ÄLÔ½ä3â¶W÷6Ì/í}øæë{oygo#â€çZ:7ú—p£ÿ`Û‰×f   0707010005c81d000081b40000000000000000000000015ab2175400000db3000000660000000000000000000000000000003c00000000root/usr/share/doc/opensvc/template.service.disk.md.conf.gz   ‹      í]moã6þ¾¿‚À¢p‚³Ýd¯iwq¹v,ú²ì¶÷¡(J¢mÂ’¨’”îÇßÌ”('ŽMœµhëXÒˆœç™7r¤¾|ù”Ÿ/Ù~P\&Í|Èìª¬È/î	? îiu÷âwœìË³?^|Có}3~ñG=«¥ÒÙE{óº–=áµcRUñ$Í>êZà-þ¬¥í~à¹¡#•Vi¤*e9½èÉÄ„×¹mGý‹*é€,gBKËËÔß(|ÂÞ°™àY…`0a¡ÝqS‰TNd
çLE	×¦¤¸ƒIáŒ3"ê„YÅj#ØRÚüÆ³‚qcDsb©*
^f®ý†Î}Í&JmÒp&æ³j8¸¥àpà¹õ[€…FÅÈT˜;4jÁm£gT3*Ôü%ü÷KdgÍ·óMjÏÅBä½Þc½k\%½lÕ9;1Bø#ðwäð&J³ÏkaN'î5ÉÞˆE:«Ëùz™Ïé„ù_±Œ1ûÎ¸©*B[A¾jž0„M«ºÌÜ/_w›×ìüÕ?æM…¯Tm{|î0ÒÌZ‹“wo¼0Àüy#ÆçF£áv+_–VL…¾©²¦p¢&NuŠ+NÙ ÈÙ&8*¿(ÏŸ!}/&²DOÔžX	m¤±¢´0t#ô‚["j’Øo$ø]Õ:ãÈ2>ò9È©´T0¾S 7‹ ¾‘,Ý/s(‡ƒs")¥ÊÄÈ2	ã	ç’ósWÝkm¥ºª´Ee¯x¢ô¡zÁÍH7GžÊò@É231íhÙÿÚûÜ2ÍD)¸UÙáÉ; DãX¸~0dï+Q~øí;0Ç<g^å0&#;¬!cåÀáÌàie	TãZ;oâú³ªN´åz*l$NU.
à*ÝÎ°L`uUÁ¥@.iÀéË
 ;æætÜN&4˜ > O'@Ø!¬“i€Úd‰`<µfä„9r4™Q$+˜K8X1w]Ø°ÉKM¯Òª>Ô¨ñ‰~ê¬ÍÐ+3­`çê¯ª)ˆf¿ã˜ÿèô2ÏÕ281D
‰,¥|å¼QãUP½cöü{-kOáÀð„È	ÍË©`ìlx>|…?œ^‘Gr8aÀ6ƒ	%RæþÀõ*½¢èaâ ôsô;Âü`p·­à®Ìì€³¼CxÇ<ðG¡K—+à`]¾ÍÀ‘Cö—Ùr&Ó|¯y<€ŸÏÏ^}ÅœþÁ·—pÐŒ¬¡ 2³5‰!¥©Qvq‚Ž3LàW/­‰=Þ#HE'ï“ Ê[!"À€0¤d.‚J0B4äŽwe‚aè0,:”ö}}¾}ÖÊòž|»úÌ#Æ¤@Pa0@OMå;â“biß‘&ÁM¡¯˜œ°/âÄ´nÈ³¥kxbT^ƒÇ_„Iè–À8=âšÂ
=f?€#7œ!’è'YÖ7,¥+ÌÎÁëqHyê2—…´xoÊ™bi
X/·Æâ.Äq´µV!S­Œ ÍL¬‚Hœà`x 7©²1ûúì‹Â9^üE)ü€:4n,xÎ«Î‘4»TîÜ˜óDd¤¼“~D½Rª¸‚[­>ïÊàá“GÏ{É&9Ÿ²“3Œ‚ç§è-¸KÔ‚ÁŸ2iÜwäÎûÚ"æ>ÆÎ¡@i|,¹ç+åÜNNÒO‡É›¹qò¹µ”õ[ä¸æÂyàH”ïä’áÎ+
åÁ‡âwY"“0íÜ$c‰ËÞ¿ÿ9äé<6ñ£‡ÁÁe!î€ú f…ûº9@h Tcãr…™©³‚B¦øgáµÑ¢`+Øù¸Í'bÖâBÞŽ“q÷ì~(»q!¶Cí·ôÍ.QÿV^y"ã5ÀdÅô©'QmGï'£ŽÀøÌZNÁºV¸P5±æü0}¨¸AJÅÜ ´]È?£Ïv˜%¯*\ÁéSÏSä.ø ã¬Õª˜aá«¶cÜ¢+^³¯îCmÑõAõß²}[6Ijïì³¯ÊÎD ‰pÖÚ/¶ÿ@¬Eì°ÊÙê’|.ÕÕRÈé¬§×“8„A<gïÞÃsN¹´Ó®ßÃ»  íRˆð¡”÷ û
6bE3Y:îñÑ¢œw²@)Ëp-ûl#œ{ÜAâS,ëhYÚbÒ¨‚úÀ±7+³GÊüD"&è!&LÂtXÂÓylË®ú+`®³Ù×½ òž&Ë§Ç”¡D=ùýÓ}âfp¿Ss¸u¸	ilÌ.u"­æüVÜqíñpîÐ!á‡ÐYÇ¿õÐŽ
¤ù­„F®,'`Ã0A‚9Ó`Þq“ˆ_H×)Æq[ÀÝÀøBI(—y	%€¥­HðåSd’;#.‡´*¢ÛÐî^S›¦iˆbôÎ´ü’Õh´¯d6í2C"ŽÏC<ÏÙwZvv)i5£½ÎiŠTë¾Þ¿¡èÿè4{˜ûˆ—ž-Ë T0é$ï°)·¸pÉ ’©„ÿÊIì‡;%¸,ç
mffªÎ3òUØWn…£±cJ˜ˆDF®}Ëvœ¯èÿb$øè;E®ã¸€Õ!Ð­ûLž€òpšYm‘:ñîÎ;k‚¿”à„ó%_Œ;¿ èú<èõõØuù}ÿö‡Ë_ú8$j,g*ïìúù{‚ 0Æq<Þ[ÕÅLÁx™O,Ê,eUåý¿¡
Àˆðgg¡J[k˜mbM	ã¼H‹ìâÚÔYÐÌ"-¦šû¾–¼oÂ¨®ÝV¸0Ã.æË‹k?Ãq83ˆwÚØØN‹¢–tS¢à…ëÈí@ÚÛGrÚ¸U³ëV+î‡Û0»EÞ°0¶ÅÚT…ÊâÇ·¦KFAkÜÌmWeê÷Ó!ÿ[AÀDLMK¢oñÀõ^ß<òüë\·ÜNí‹Un]m±á!Kê&‡›p™S[\® ¸N,¨Ñ9,põöW#&uNŠn)Ë9ð®.*6‘¹p«±1i'mG\$2î Û¦¬PÌ’ãk';L.^:î|j¿‡¸_Rhp
§¶!´s°,ã"9ø±®·":rO‚«ÆÔ<Èh}8úNªW¥¬ãìº}|Ö‹ë+ }@Ò-ý€P»ªd
ƒâ%î¢E#Ü5–€Õ²«R•#ÚòÍèÙÐ¤SA¡ÀCÃ)~Ù(’S¨ºôíÂè{Ñ8&Ø8×aÛ°ÎW8²	Æ–ñ˜-¹&j\°ï¡v±ëSt·nÊÏùÂ/Ú-c
úÙÖÕzt„”½|pyä£iy.«nÞ¸5“$ÇV—1áÓ$+ÿñé¨†šì$f:ë:¥U0˜¦ö=wèy»Ž¶à–Là×²#§ÉæBT¸7¨ªÕýñê-·Ü÷z†q;½ñ§²gÕó³*ÒúmöDdxâ{ÜÏç›ŽþÃŒÂºö	œJ(Þ}º7ãX©i˜Ø
‚õûn t`-&w–áÓD¤¡ËñSHenq.ñ)lª4#¡R»³.óü.i~› Yé¢z7þ9 8…R±+‚RÑ[í`nÁ_¼%ü‰2åUÏÒ}°Ô­¸~ïBßkå²4ì¸7ëµ:6œÈ[@šÖ,B©sË1[±¸R[ r™åªúžÚ ‚åjåö	c‡R¹}¸¢˜î·=sWÇéöõüÊeó fø©–®HÜˆÛÝüöNÇ}Ýç­Æì­ÖJ7Ïà“Öue#æ³“÷/!WÊØ½‡£Ç'Vè}€·†Çô êº'0¼õŒ1^Ê}€Ù=ÄèzÜorO[‹íævœÝdj~[â1fú¶™XÕãÌë±P5ìbVêøÊƒ²ªfcï“JU;ÙTÔ£LêQ8î*ÁÆ@ÐÊU_š=oi¶¸MˆìŠc_¥=k•¶ŽªÓ:ð÷(>OÁ¶›1~’)ö>Kí¶£~Šö¥Á~Ë¸ÝŒo‡â uÛ^+ºîA°õÅÝž‹»]Mm÷ª¡¯óö\çílgÛ0‹2Ð+¯Áã{°g?ˆ-gÒ
SñTŒŒÀwW`;Zxœ_%!] àX·cËÓT &º.Ëîãôœ"Mšrá²DêßTø„åúfü·ZfoN¾¥~æ7Ãñxìßó…»®VUHKoÉÔÕÐXlW««Ax¯LW=§ã_ 7©½Òf½5¥¡hkß[ÏŸ½òçHØó îPÐóf¯¼!ïÄ™L‰Ä=„3w2å–¬ÐaÑà^¾¨ª§Ëžé¢ªCgKD‚û¶—_éås0¥ý/Ü—¶åE©þ‰t¯wB4Á§ÚwG€b—äÈ½@È=ú mˆµmmcøÒT÷:ÖßçÄ—§¤ pø?ì}ÍNÆ[q“æµ‘1mMÁi^Zzžï”Pûû¦÷0+ÿVªHVÅíŒJ%z[•ë‡‰Ü¿+êˆ³·7 {^#‘ë/@õ/ªm,¡‚©ûD×È¿ÓË?²N?°%§æîHTS³²àžêØÚí^ÃiÝ[ƒÅ”ÑÃñ‰À¡¢,>ìƒ/Æß#q‰‚‰º—|Æ@ÜõêÞ–ùá¥½±¿ýúÞ;ÞÙÛŠØá¹–Þþ%ÜèÿÒáÓÁöh   0707010005c81e000081b40000000000000000000000015ab2175400000e07000000660000000000000000000000000000003f00000000root/usr/share/doc/opensvc/template.service.disk.rados.conf.gz    ‹      í][Û6~Ï¯ N°¶w&Û¾L›`§m
›6$í>…‡–h›°$ª$eûã÷œCR¢ìñe:ötÜÊ@[—#ò|ß¹‘çùóc~ž=gGü ¸TšyŸÙU)˜æ©2wÄˆ;®îž}ÆÎ>¿øõÙ×Ôß×®ÇÏžaÃçbµT:½jž/s>NŽøA™D•|œ‰úY?ðÌ<£Åo•Ô¢iÅ']Ñ‰R«…4R²˜^µnIÅ„W™mÚý“*è„,fBKË‹Ä?(|ÂÞ°™àih…`Ðe¡ÝySŠDNd×LE÷&xY¢Š…Ð‘üLKƒG›ŽšêÆXÁsaØŒi&R6^1;“†-¦Ð;£*ˆ![Î¤¦ä‰ˆ(cDÉ5·"‚ø¯ö£‰RÛJ2)
;’éR}æB)ã;R	“)³ŠUF€ú4ã•ÁQ™pcKig J@ÍÒz­ÑÝŠ‡#úØ©=¨Ý+•ž)5'­£†“#-ì.8"SØ&<`',™JæqLÀ)€ã‘)øã!n“¬2r!Øÿ˜™qh.|q|ºCç*5|‘[3ˆ* ¨%îbäž5²ü¯n'(¿Ejù¤HY°Ï=§¥Þ¯!ô|XU‚™Ä!&ó`(ÂŠ4¿Û^Œü¯xªØ„ÈÿèñÝ)eG|ÇPó?~‹
§_;µLwÀ¤rnÏNÛ¯N-Ö5tã³¯+Ðê«m:-5{eÅ<=¯éó{1‘$‹¼¹°„À	‰*_H;!ºXZkœ{HG£Àû‰ÏAN©¥‚ö­˜Ó kQ‚LÄ ‘aÇày©CA¼Yß8£!Ö$´'\K¡ÜÝµÓd
5*µyiG|¬ô£˜ÍQ‘®Ïœ*¨ck!Œ×ÏÙpcc¥àQE‹'ï ÈÁ ªô,Üßë³—>þòä`YÆ¼Ê¡MF¶XC W&*Ï~Y ÕxÊÔ8Nâ	ú²*N´åz*l$Næe&rà*=Î°T UYÂ­®~Ê¸±.9y!ÌíËaÓ™HPo‚úè> aû¬·N¦j“ã‰•,¸ô‘£ÉâÔ›s°$bîº$ ±`›—šŽ’²z”úñüÔCÒ(@ß£’2—ÏØævÞsejœ¶8Æ _Ç²@”²•óFµWAõÙwð)O\L×—pC%;rBó¢Í»è_ö_á‹Á+òH'€l3yæéÀõ*½¢èaâ ôcta¾7¸ˆÛ^p.qî ÞñÁFVL…nÁÿ/¡—+`cÙ‚g±À‘CA“öq,™Á÷ŠgÀ8|yñêKWÌ€A¿+Zá ŒU¬1ÒV$úL¦.DÙ	Ä :~,š€3x\½´&öøŒT |“Ã¤"4#A‚™*ÁQ“#|8\ÐH“‚fèÐ,Z”ö}u¹}¿UÊòŽ|‡úÌ#Æ¤@P¡1@OD^>	ˆÀïH“à‚¦ZU%“öEœƒ˜Æ­ ¹ ·t•U Ãñ$aÒÚ§G\ó\X¡‡ìpDâ–c‚ÓG½—EuËºÃôÙà¼‡”§*2™K‹Ï¦œ)–¦€õ8|³Öw#¶£¨ò1ÐºŸËD+#À@S« '8èMªtÈ¾ºøâŸp—¿(…ƒ¨CãÚ‚×¼j]I³Kå®9ODFÊ;é»#êH©|¶Zeùw’ÿ@Ï{Í&Ÿ²/_¢´à.Q¥Ò¸ïÈ•EÌ}ŒC€Òjù4^ëùJ9·““‚ô—}ÈäÍÜ8ùÜZÊú-ò\sî<p$Ê‡wrÉðä…òàCñ»Ìs‘JèvæâgO ‘~Œ¹F:güo´àÐ8¸-ÄPÔ¬ð\×nà¾&ðL„–™jlVPÈä_c/¼– z@´l»6ùDÌZ`úåŽ“qwì¾/»7ÆâÞRÐ7‡Dý¼ò…,"ÜÇ+(¦_zUvða2hÙ ´È¬å¬K`…U£kÎÓ‡’¤TÌBÛ…üúìa‡Yò²Äœ.õ¼7Eîr€:Î­ú€¾Za;Æ-ºã5ûrj‹Î¨Ÿ„Qÿ!;µe“¤æÉ>ûª ì öZ»æÅöˆµˆ=V9{]Â8›K5Z
9uô:ŠCø–æÛÞ}€.fœri§Ý!ûÁ¼ íRˆð¡”÷ ú
6bEÝY:ïñÑ¢œw²@)ËÏp,ûb+œG˜m»-ªÄ§XÖÑ°´Å¤Qõc¯Gf!”¸Öf,&è!&Lâ¬è˜Ó2€Ú–]õ—C_-f³	ŽzAäk¶4Ë§ç”¡D}ñù×—§ÄÍ»Vs¸q¸	ilÈ®õXZÍ:øFÜqì‡Öˆ;tHø&´ÆñÂÆO=4­i~*¡–+‹	Ø0t`N5˜w\ÁŒÅŒ/$ˆkã8-àžßc|¡$”Ë¼€ÀâxA¾|ŠLrWÄåVyôš]À{*ÓÃô"	QŒaO‹¿s¢µöuÌ¶	y¨@Äùyˆãøü˜Mx§Ek–’F3šûœ¦Hµîëî	Eoüg§Ù§9xéÙ²J“®Aò›r+€‡ì*™HøWNb?Ü*Áe17Ph33SU–’§(Ã¼r#SÂ±ÁÀ¯•®}Ïtœ¯èÿd$ø#ÐwŠ\Çp«%B [÷™<åá4³Ê"uâÙwÖ)Á	gK¾2 g~AÐÍxÐ››¡+ê óûþí×?¿ÿÔ'j,g*k/¡uÏA¡Ã¸½ÕÅLA{™O,Š4eUéý¿¡
Àˆð³5P¥…­4ô¶J°¦„v^%yzucª4hf‘äSÍ†}_q…ð›Ðª7.Ì°«ùòêÆ÷pÎCâY‡&66Ý¢¨%]—(xá8rÓæñ‘œ¦!nÔì¦ÑŠ;°	³äc{¬M•¨,~~cºd4VÀÍÜÑvU$~Nài:ä+˜ˆ	Õ¦n’Ô-ñÀñ^¿*xäù‹«	×,·Uûb•KkwÙpŸ«:‡›p™Q¿).SÜJ,¨Ñ9,pôög#&UFŠn(É9ð®ÊK6‘™p£±1i'MG\$2 Û¶¬PLÇç·œìirñÚypçSãøÝÇø‚BƒS8-ÂF;ËR. ’ƒk{; ¢#÷$¸jLÍƒŒÆ7£o¥
cáª”uœÝjŸ5BÁâÖÐ¼@ iŒ†~@¨]•2ŠAñ¯¢A#œ5–õ€Õ²«B¿$_¶M:ä
<4\â‡"9¹ª
ëÊ"ô½h\8÷U@–­°eŒ-Ã![rMÔ¸bßCíb×»èÝ”ïó•o^Ü´c
úÙ·ª)0ôì&{RöòÑ­È#MóËsY¶ó^À­î$9¶ªˆ1Ÿ:Yù§HK5´ÈNbÖ ©³®ƒnj¿æ=oÛÑæÜ’	ü\´äÔYÂ\ˆçU¹¢º?½å–ûµž¡ÝÎAoãT»G«‡U‘Ö7Ù‘á!ˆŸ±›Î7üO3
{üéÞ#8•P¼ûtoÆ±RÓÐ±3ê)®»Ð«h1¹û°Ÿ:"õ]Ž·˜Bú(3‹íøxý“HaR¥n	•’¸:ë:Ëî’æ§	•.ª·ãŸŠS(WEP*º±Ì#ø›÷„?Q$¼ìXz
–ºÑ·Þ»†Ð¯µrY®¸·ëµ:.8‘).©—fJ­{\ŽÙˆÅ‘Úª~O70vkNh‚–©•›'ŒJå²ï'ÒóögNbtÎ‘îTï¯\×/`†ŸhéÖ‰[‘àr7?½Ór_»¼Õ½ÕZéú=œcÒº*mäÂ|vÒÂc÷r©ŒíÐ»?z|b…>xkxìA î ;‚á­gŒñPî=Ìî>F×áöp“;l-,ö›Ûy®vxB¦æ§%bfa}Ã>ë°z˜y=ªƒCÌJ_ùñ¤¬ªžØûÝF¥ÊƒlªêA&õ œ»û4F]iö¸¥ÙVà¶!r(Ž]•ö¨UÚ8Þ«NkÁß¡ø8ÛaÆø»L±ƒðQj·Íð÷aWœ¶Œ;Ìø(ÚPw°´¢;Ðàî[WÜ¸¸;ÔÔ¯º:ïÄuÞÁv¶³(yžß‹=§A¬ÙxPïA\¿Îƒ[IH· pÌ…›±åI" ]EûuzÎz‘¦{u¹pÝ¢õoK|Ãr}2þ-Ó7/¾¡õÌoúÃáÐïó…¿ÝªV•KK»dôª²o,.W«Ê^ØW¦-ÞÓñÛ ÀEbÃZi³¾4¥¦hïº·Ž?'åÏ™°ç^Ü¡< ãÍIyC:>ˆ3[˜‰»gîdÊ†¬°Â6¢ÁN¾¨²£Ë‰é¢Ê§Î–ˆ»./éåc0å¸m?Íui{6Jõo¤{½¢c| ´ýÛú»Ì<¸„Ü«Ú†øáQÛ·l7MuÛ±vø>&¾<!€³À?—2øŠ½ #lëðû ­)¸"ÍKïó½$Ôþ±mfåw¥Šd•ÿ‚ò»U¹õ°3‘5A¤ùó!ooA$®yD®o€ê7ªm^,¡‚©ýF×Àïéå_Y§lÉiqw$*ƒ®Y™sOu\Úí¶á´n×``1%GôrüX`ÃPQ_öÁ¤ñx$n¬ £n“Ïˆ»¶îm˜6í}øæö½wìÙÛˆ8à½–Îþ)Üèÿ+eùtj   0707010005c81f000081b40000000000000000000000015ab2175400000e87000000660000000000000000000000000000003d00000000root/usr/share/doc/opensvc/template.service.disk.raw.conf.gz  ‹      í]moã6þ¾¿‚À¢pg{½ö>¤›ÅåÚ-°¸¶9 ÛÞ‡¢Hh‰¶	K¢JRv\Ü¿™!)Qv;MœÆ…´-iDÎóp^È!ûúõS~^½fOøAq©4ó>³«R0Í—÷„÷´º{õvöõè×W_SÏ±Ç¯^a³çbµT:=kÞžŠ…ƒ'ü zL¢J>ÎDý¦ÏºxA‹ß*©Eºq¡Ôj!T…,¦tñ;žº’Š	¯2Û4úGUÐYÌ„––‰O&ø„}`3ÁÓÐÁ ¿B»ë¦‰œÈî™ŠžMð¶D¡#ùFXÒ¼Ù$ð3g™4–©	jK&‚•ÜÎfïN>œ½OýÐ¾–ó²„Ž˜~Ä•åLZaJ7QrÍ­H‡ìóLá6Œkhð²)¯˜ášÄò"D™ÄHP%^ätÆJ•ÉdÅ$H(ËLÂóVáóùzò5‚4xÿ}‹-ú­Y$ÃttF?)Í“L¼TÔhtºyÛé6ò$Z@'®“××¾*—jÊ<€Lõ•C±i¬H*ZŒº,Ø÷²¨nûu×B¿P–ñÊª€Mx–­¼–Sà(X£æðÁ‡`À'
^‹„ú4A#þ¨'v‚,ò½~Ó'N¡ò¨J'"”j	"ØRf™'²žÍü‰db•…Ö2æB"óÏÑî ðÎ©x‡* ñÑ‘æI-PÄ‹Ï YT¨˜[ffªÊÀ8“ Ñ 90Q±CöQÂ7m`E•£(xQl#Vù­›(íÌ Io ”Ý†ÆT«ªìàˆà 'ú®˜mx€Î;8"8P!Ò`;L¤ü ÍŒ/ ˆf¬FC¥E‰N²°ÞjEh¨Äò,–Fˆàw ä£Ñ6@~w}ë 	€ BXÁs±áZ0Éd1§`$ø¥ÆMd¶áÇÀyNä´‚þ²‰V9Ý;ÍÔ Ò2”uhCoÆh‹£fœÓo[Ã•RÃox¼oÅDt47ýDº åVp	ZC£åóà#ä>ó9È)µTÐ¾Ã@$Ž]™X`˜@ïK
Y´Gd¡R1D„ö„{ÝÈ¤§Î¡j¼…º†á.òÒ^ó±Òöè~òP”,SÈÌYÓZö¿æ={ÅªM<Ù³ð|¯Ï.KQ\ýü‹½Ê7S’%¸CÆ#€Ã‰Ês„_@5ž¢åõE¼@ÿ ‚Cv¥À°hËõó²ZœÌËLäÀUza©¢ØÙTe	¹ÐüpÈÛr ;æöMG‚zÔGó9€Fª·N¦j]?O¬\P<}ä8d‘¬Úö9Œ$bîº¤]q9½NÊêø©{íÔX›
 WjÁ>©(§Zý‚mþµEÐ‹,SË:Q†CÖa¯cY J™Ï¤k«‚ê²oàßètâ­¾…ŸókðUÅT°36êŸößá£Á;²H'€Øf.òÌÃ	êUzEÞÃÄNé‡èwŠ-
.â¶\`Àµl÷'>žâ;œ„`nŠÉÿ¿ý”‚k,[ð¬¢8R×´3hÉþ®h^~>½û’9ýƒm/Zî ŒU¬1ÒVä$úL¢ËÖÌZª€ høáà+è*˜ziãà”Þž
¤¢‘÷AEÀà*—K$˜jd"¨=DMŽðáHpÄ*Ê};|S
Ø÷Õé.öýV)Ë;òík_|™PŒ‹¤@P1wHyEø$8÷å³Œ`‚\².'ì‹81YrAoé>6*«@†ã/HÂ ´!0¸†,Ê
=dß!·œ>’ˆæ
YBO˜>œ‚ÕãòTE&siñÝ3ÅÒ°^.6ÚâÄvU>v“¹L´2hjbDâ‡z“*²¯F_üîñ²à…p8k:4®-xÏ»Ö‘4»TîÞ˜óDd¤¼“~¿G½V*¿†[­²Žü÷’OË{Á&Ÿ²“zÁÓ7nÆK¨ZêH¥q#w.+Zñ>vy€ˆ§º0Yäž¯s;9)HÇ)hnæÆÉçÖRÔo‘`šsg#QÞ½“I†7¯È•ŠË<©„ng¾!õÒÉåå‘ ×Hg±áŠÿŽ#84~Ô9+¼×õÁÍ{Çž‰Ð2SÍ
™ükì…×xðV€­`§Ã&žˆYLß>Qáî¦ÁÝ±û¡ì6òwÑ¢öGrúf¯¿WžÈ"Â}¼‚dú'Qe—“Ak@ûÌZN§¸ƒíL¸kÆÃ‡’¤TÌBÛ¹ü}v°Ã,i„uy(Eî2€W gV½Ã_-·ã=qÎ¾¼µE7¨_Ä þBvè‘M’š7ûè«‚´sŒ‹JØkíšÿ@¬El0ËÙiÆÙ\ªë¥ÓYG¯'1ÿ‚(xÎ>]B3N±´ÓîýŒ`žv)DøPÈ{
 }±¢î,]÷øÆhQÌ»Y ”åG8—=Ú
ç”TÜ…eâSLëhZÚbÐ¨‚úÀ°×3³GÊìÄXLÐBL ˜ÄåÁ1OæñXvÙ_}µÍ&8@è‘çl+h–OÏ)CŠzòË¯o‰ÛzaÕE]X…²=–Vs„¾‡wœûA<œ9tHø&´æñÂÆ/=4­i~)¡–+‹	Œaè [E¦–Øˆ_H×JÆqYÀ½¿ÇøBIH—y)€Åù‚lù™äîˆÓ¡°”é^C«øLez^$Á‹ÑOØÓâ-'ªQkÏkd¶°3q|âil~Ì&p¼Ó¢µJI³ÍsNS¤Z÷çýŠ~ðf_æ:â„gË"(†t’7Ø[\8ePÉDÂå$¶Ã­\s‰vT–™º_Wn„ã`Çp,‚3)1 ˜öËq>£ÿ‹‘àÏ@ß)rwÀF-Íºä	(§™U©¯î|²&ØK¬yÍ–|e°DD!¼ìæ,èÍÍÐ%uù}ûñ»‹Ÿ¾ÿìŠO–3•µVýü;APhã0nïFv1SÐ^æ‹v]®U¥·ÿ†2 #Â×ÖD•¶ÒÐÛ*ÁœÚy–äéÙ©ÒØ¡™E’O5öþÄ›¡U7n)L˜agóåÙïá0\‡Ä«olºE^Kº.‘óÂyä¦!Íë#9MCÜ¬ÙM£÷Ã&Ìn’7LŒímªDeñã›Ó¥AAsÜÌmWEâ×^¦Aþ‡‰˜PnêI]‰Î÷úªà‘ç/ŒÎõ‘ÛÊ}1Ë­Êc¸ÏÆUÃM¸Ì(‡ß—)p®j4XcÌ~2bRe¤è†Ò™œïª¼t5e4“vÒDpÄÅ@2 ã°m‹º Åt||åd/“‹Î‚;›ûï>NÄäœÂ©lí,K¹ Ov¬mí€ˆŽÜ“`ª142Û†¾*Œ…ËRÖqvÕ>>j„„ÅÕ¸zxOÒMý€P»*±|“—¸>ˆ&pÕXÖÃ*GW…*´ä›Ò>%Ð¤SA®ÀBÃ-~Ú(’“«*”B¢íÅÁ1ÁÂ	|®* ²l…-› oÙ’k¢Æûr»ÞE÷êf@ù>ŸùæÅMÛLA?»ªšCnÀa/j¼\¹Š<²Ñ´¾<—e;îÅMA¡“dØª"Æ |ê`å¿ž"-ÕP‘Ä¨Bg]%4ÝÔ¾æ-oÛÐâ¶?-9u”0¢ÄµAU®Bý|_qË}­gh·3Ð[·Ž«žŸU‘Ö7Ù‘á1ˆßq?œm::ø_¦¾Šö=Q	É»÷f35[3Ã½lXw®«h1¸»
X†Oí‘ú.Æ[L!|”™Åv\]üè=RXT©[B©$Vg]dÙ]Òü2²Òyõ¶ÿsAq
¥bU…¢å`nÁ?¼Ãý‰"áÇ·¯ê(Xêf\½w³õÄÍº(+.Äíz®Ž'2Åº4‹Pj=ãbÌxG‹U¨\¤Y‹ª¾æ„V ˆ`™Z¹uÂØÀ¡T.A.)¦÷íŽœÄõ1{ºCí_¹¨7`„ŸhéêˆÄ­H°ÜÍ/ï´Ì×}ÖjÈ>j­t½ ×˜´®J™0´ð¸
¹TÆvè==>±B¼5<v PwÐ=ÁÀ[ã©Ü»‡º·Ç¹§€­…ÅîávœÕ/h¨ùe‰Ç³Pß°kˆuX=nx=ªƒ}†•:¾ôãEªzaï*Uî5¦: 5¤…S@à~ ÆXZ¹îR³çMÍ¶·‘}qì²´gÍÒöÀñAyZþÅçIØöŒh(v>Kî¶ç0ü#ƒ°K›Æí7øöHÚPw°4£ÛsÀ=¶.¹;pr·ïPÛ?kèò¼çy{³]˜Eèµ×àñmì9bÍiÆƒú4ãz;%!] à˜·bË“D &º*ŠövzÎz‘¦{uºpÑ¢ú·xœ£Y_Œ¯eúáä=Õ3è‡CÎ~÷'*çÒÒ)½ªì‹åjUÙçÊ´¥EÇåâEbC­´Y/M©)$ÚY÷Öñç ü9ö<ˆ;t¼9(oHÇ{qfS"qáÌLÙ*l#ÜËUvt90]TùÒÙ‘à~Ç„åå×zùL©aÿ×¥í8(ÕïH÷z'DÇ<ú_?¬íeæéÀ ä¶>hü‡GmWÙšêŽcíð}N|yB
 cÿó•ÁWì„`Œ°·IV¹}ÐÖL‘æ…¥ý|oµ¿o;‡YùS©"Yxþ5¥JtZ•«‡‰ÌŸUDÙÇ[éNE¯E®€êªm6–PÂÔÞÑ5ðgzù-ëô[r*îŽDeÐ5+sî©Ž¥ÝîNëNSpD›ãÇ†Š²¸ÙÆß#qcu‡|Æ@ÜutoÃüpholÃ7ï½ãÌÞFÄûZ:3ú—0£ÿ2€õöÂj   0707010005c820000081b40000000000000000000000015ab2175400000d2f000000660000000000000000000000000000003f00000000root/usr/share/doc/opensvc/template.service.disk.vdisk.conf.gz    ‹      í\koã6ý>¿‚À ÈÖö&³í—´3h¶ES`¦ÝEáÐe–D•¤ìx±?~Ï%)‰r&±3‰Ó¸PÐG"[Wä=ç¾È+>þ?Ïž³ü!q©4Ë³›J°ý~Oqøq«»g¿ÓŸŸþñìK7ß×~ÆÏžÑÀ—b³V:=ïž_q»Àãü!™DU|–‹öIt-è-þ¬¥éµ*­VÒHUÊrî>üŽçÆ}’ŠŒ×¹íý³*Ý²\--/“ðœ\ðŒ½aÁÓf‚a¾BûÏM%™Éß™‹÷&N+x‚Ið_ ¦2f—V2¡›Y&sÁj#RÆãl%µ­yÎ
ž,d)µ&ìÃBD¼ ¥~]ªT”¼,á%›VÑ?FXVW¸5Ë„¥u70U2¡Œnœ``_º«¯Y¦Ôèi\ûKákQzø}+2 B µ_¬„6ÒXÒ±Fè·"ijÕi\WµNHåí?ð%äTZ*ŒoÃÔJøï’Ðbœ‹•Èibx^êQ`¼Lé;‘d¢Ê¬Oó]®»ëVxK5­´Ee§|¦´=:¤ÛOjF¦”Ê”[aÎ»Ñ²ÿuÏ¶U4‚™RxTÙãÉ÷™³@â‰Åý'#ö®åûß¾ak™ç,¨c2²Çšµ„=òØÎÃ7U¿,A5ž’áí‡ôû—œ°÷
.Á‰¶\Ï…ÄÉ¢ÊE®ºÇ–*0 ã¨«
·‚\Ò°œË
‹½æêå¤›L$è$#}œ÷r„±“m24Ž‰'Vb† 'æÈÉdÆ‘¬Æ<ØŒÃ’s·%Ä€›¼Ô|šTµ9:öÞê§îÀÚT€^©é'Z@ßÓjÑìwó=‚^ä¹Z7Î†Fœcˆ¯3YJùÆ{£Ö«z'ìü—q» î+^9<!qBór.Ø9;^Ñ…Óñ+ç‘<N€“.Üf!ŠÌÃ	õ*½qÑÃÄAé§è:Á|gp	·à‚S³€øâÛ!þH°‘¥s¡{ðÿ tés,[ñŽœÒÁ[/$2¾Ú £pùìôÕçÌë¾½ì…@3ž©¬19(‰¥©Á…(;AÜ AÈñãˆîS¸ziMìè© •œ|H‚(“¥ˆ@)."A‚hDùoP	Eˆ–Í'‚ûÜVaº‡=J}_œíbßŸµ²| ß¾þ… HÁøA
ƒ=	yåðI Â‹ÎÍµB"3öYœƒ˜Î­€\˜­»‡ÏŒÊkÈðü5tM`¯Ò¨¬Ðö‘¸â”àŒˆD?Ê²¾b‰»ÃŒØø^#å©Ë\ÒÒ³]ÎKS`½\]‹¿‘ÆQÖÅôÃô™he45±
"q®Ô‚Þ¤J'ì‹ÓÏ¾Æw‚,üåR8\ ?úÎ«Þ7"iv­üwcÎ;"å½ôÛ#êT©bŠ[­òü·’OÏ{Á²œÏÙ‹SŠ‚g/É	Z¸KÒ‚¡K¨ÜýïÄwµ%ÌCŒ]¢ i­|*yà«Ë¹½œÒ_ŽÉ›¥ñò¹µ.ë·Ä¸æÂ{àHTïÎ%ãÉÊJ¿Ë¢©Ä´ó0”Í|ŽðîÝO‘ ?Hï±ñIø›,¸nkâÔ‡šÏõs@h ª1¢™©gfƒB¦ø’f´„èhl;›tùDÌZ0ýlÇqì¾+»ü¯èQû­úfŸ¨-¯|!Ë÷ÙÅôË@¢ÚŽßeãž`ü ³–sX— 
U£[ÎÒ‡Š¢TÌ‡¶ù§îg;ÌšW­à©ç)ò1øÐqÖi5Ìfá«¶cÜ¢;^³ÏoCm5õ“0êd‡¶l'©{rÈ¾j”3Ñ¬µ^lÿ±V±G *g§K˜åK©¦k!ç‹^âþ,xÉ¾‡)æÜåÒ^»öy íZˆø¸”÷ MB±¢¬û<à£årÞ›¥,?ÂµìÓáü„uì}Ðr•øœÊ:·,m)iTúàØÛ•Yä‘²€Ÿ˜‰Œ<D†dÓa3ž,c[öÕ_¹ZÊfZ?pè5"_³A³|~|A%ê‹ßÿxyHÜ-à÷j¿—9MØ…žI«9A‡¿··½;ôH„!ôÖñ„MØzèFia+¡•+Ë6Œ	úýKóŽ+˜™Xð•„¸^1NÛþù'Œ¯”D¹ÌK” –Öøò91É#.‡´*¢Ç¸Ýº§6'”^$Ms—h¦å?¹£šíë™ØFˆ8>ñ0>?fï¼ìíRºÕŒî>¯)§ZÿëíŠÁøN³OsñéÙºl”
“nA
ÛåV€‹–ì•L$þ/³Ø÷JpY.
mfªÎSç)ªf_¹NÆN)áL4Á@¤Žkß±*ú¿	þ
ô½"·q.°ZGrë!“w@8Í¢¶Dxwç{k)á„ó5ß¦_º¼„½¼œø¢™ß·o¿»øõÇ#GõBå½]¿ðLjÆ8‰Ç{­ºX(Œ—…Ä¢LcQVUÁÿWÑüÙ[¨ÒÂÖ³­ª)1Îó¤HÏ/MÆÍ¬’b®ÙØ°¯ð+uÇ¼iFué·ÂàÂ;_®Ï/Ã'Íç˜A¼ëÐÅÆnZ.jI?%¼h¹H÷øHN7¿jvÙiÅ_¸³_ämÆvX›ªHYüøÖtQ¸µn–ž¶›2	{OÓ!ÿ¢0	W›úMRßâAë½¡+<
ü…un[n¯ö¥*·®vØðˆÍê6‡Ë¸Ì]]\®|'ÔäV´zû«Y;Ew”Îå¼«‹ÊuŸùÕØ˜´Y—Á9.6$÷€í¦¬(¦³ãk'{š\¼ðÜûÔ8~h!¾t¡Á+ÜµÑ ½ƒe)ˆäðc}o"zrg«¦Ô¼‘Ñù&8ú^ª0¾JÙÆÙwû„¬‹ï+pû9œÆÜÒ„ÚM%PÅKÜäh×,;«3gW¥*ÇnË7u’Ð¤WA¡à¡ñ•°lÉ)T]Z_‘ï%ãÈ¨q‚î«K@–ohdÅ–É„­¹vÔ8gß¢v±ÛSôî*Ìù</Ú5cjô³««©aèÑL#ìIÙË{ß‘ç|´Û_^ÊªŸ÷·v’Î±ÕeŒAóÓ&+ÿ	é©Æ5ÙIÊ:ë:q«`˜¦=wäyûŽ¶àÖ™À¯eON›%,…¨hoPU›¦y¸Í¯¸å¡×³·wÐ7qª?£UÃªHë×Ù‘á>ˆŸq;¼o::øŸfø»{À©4Å{H÷ôF@®1±‚‚zJ}7ÔEKÉÝûËæ§H#Ÿã­æHeniï/~©ÙTiGâJIêÎºÈóIÛÄJÕûñÏ„âI¥®—Š^kóËáæáO”	¯–‚¥~õÀ÷{·†^+Ÿ¥QÇ…¸Ú®Õ©áD¦ÔÒ¶f9”z÷ø³K+µµ•Ë4ïQ5ôœ¸G°\mü>aìàH*—Ð‡/ŠÝóvgNbzÌ‘îPï¯\´/P†ŸhéûˆÄ•H¨Ý-lïôÜ×mÞjÂÞj­tûí1i]W6ra!;éá±ã•#eì€ÞÝÑã™úàmá±ë…11 { ÃÛÎã¥Ü;˜Ý]ŒnÀíþ&÷°õ°ØmnÇÙíð„L-lKÜÇÌšþ†]&6`u?óº/T-û˜•:¾òãIYU»±÷ÉF¥ª½lj ê^&u/œnjFÐÊt(Í·4»¸›ÙÇ¡J{Ô*mïT§õàP|œ‚m?cü$S |”ÚmO3ü#JƒÃ–qûßÅAê¶ƒVt{Ü`Š»wûšÚþUÃPç¸ÎÛÛÎvae Ó Áã{±ç0ˆ­Ò
SñDŒ ³+¨­y‡Ž’¾ 8ÂïØò$ÀD×eÙž³“HÓ'm¹pÑ	r/ê_Uô†åöfüWZ¦o^|åú™ßŒ&“I8ç‹þö]­ªÖ’qRW#c©]­®NšseúÒüq†þ <P$¶é•6Û­)-%@¢}oÊŸ#aÏ¸ãò€7åÓñ^œ¹)‘¸»pæ£L¹&«é°hp+_T5ÐåÀtQÕSgKD‚Ûµ—Oõú1˜ÒÂþ7îKÛqPjx#=èÝ!:£·üÙÉfû]fžŽýBþÕm›øPÛÕ6F‡¦úãX|_ž8ÀYÐâã/Øc„­¸JòÚÈ•A[s¸"ÍKëÞç{éPû×Mç0«p*U$ËzM¥’;­Ê÷Ã.DÎÀŠ:"'ìíDRÏk$rû ÔpPm÷b‰+˜úotÃ™^á•uw­¹kîŽDå˜š•T§Ön§õ§ƒÅ.9r/ÇÏŒeée:@š®GâfÊ.Â!Ÿ1;º·c~shoìÃ¯ßû‘3{;{¼×2¸Ñ¿…ý?He]O–a   0707010005c821000081b40000000000000000000000015ab2175400000d01000000660000000000000000000000000000004100000000root/usr/share/doc/opensvc/template.service.disk.veritas.conf.gz  ‹      í\]oÛF}Ï¯ (œ`%­m_Ü&Xo›Å¶ÍI»EaÈ‘4ÉáÎ%k±?~Ïr(Ç–[©UP@Y$/gî9÷kæ’ÏŸ?æçÙsöˆ—K³1»©[	--7÷ˆˆ{\Ý=û&ûüô÷g_»ù¾Ž3~öŒ†¾›µÒùy7‚Š—Æø!™LÕ|ZˆöNtC÷aZü§‘Zä7ÔZ­¤‘ª’ÕÜüžÆÉÅŒ7…íý³ªÜY-Üäª,Ü§|ÆÞ°…ày„`˜¯Ðþ¸©E&g2Ã9sQáÚÌiw0Îø°NLÍ˜Å÷•*ü5×ª©qâ×îØk6Sê6mÖ¿ý¡êlµöôù˜ÉJÆ»k¡4VTC7B¯¸ÅI“P«vZÇïªÑ™˜$¦ö/!§ÖRa|¦@jw.I™b%
šî—{¯r:'‘R©\L2UÍâxâ¹B×_u'¼•º¬µem/ùTi{tH·GjÆ2(YæÜ
sÞ–ý¯»´¬’L•Â­ªO~  Â2«Ø‰Åõ'#ö®Õû_¿ekY,¨c2²Çšµ´Æ€ã™™*K‚_V ÏÉžy{¸ÿÁ	{¯`ÔN´åz.l"N–u!JpÕÝÎ°\GS×¸ä’†ÜXV‚Xì…0×/'ÝdA'3ÒÇ	ôé;b'Ûd:!m²©`<³391GN&3NdEó`SKrÌÝ–{ nóRóË¬nÌÑ±÷N?uÖæôÊM'8Óú¾¬çÍ~£1ÿÞ#èEQ¨ut64âLC|ÊŠP*6Þµ^…Ô;aßâÿŒëÔu§ 	)à	‰šWsÁÎÙéèlôŠ~8¿rÉã8é‡ÛÁ,E9€y80¡^¥7.z˜4(ý”üN0ß\Âm'¸`À¥Y@ü ñÝ$ØÈÊŠ¹Ð=øÿ)tås,[ñŽ¼1"±õBf|oxàç³ÓW_2¯øöªn Íxª°ÆHÛ¸ 1b¢2¸d'ˆ 9~Ü±Â…«—Ö¤žîA‘
RÉÉ‡$È%¥ˆE‚ÑH"ª„"DKŽøáDp”V†ŽãðÁ¢G)°ï«³]ìûO£,È·¯!ÀR0~‚@Å`@OB^9|2ˆ ïD“è‚\ÁäŒ}‘æ ¦s+ fë®áSƒâ2<!‰’è†À4=âŒzÂ¾‡#×œœ‘èGY5×,sW˜ŸÁëq¤<MUÈRZº·Ë™Ri
¬—«cñÒ8ª¦œ‚~˜~)3­Œ€æ&UA"Npô&U>a_~ñwœdá/—ÂáÒ¡ñc¡s^õÎH¤Ùµòç¦œwD&Ê{éwGÔK¥ÊKØjUä¿“ü{zÞ6+øœ½8¥(xö’œ …»$-ú)—Æ'î¼k,abìu IkåS±È_]Îíåäþr„LÞ,—Ï­uY¿%>À5—Þ'¢Bxw.wÞ¸P}(}—e)r‰ia 9›úáÝ»ŸA~ÞcãHø›,8—Å¸õ¡fÅ}ý€jJà…ˆ#3ÍÔlPÈ”_Ó,‚–=­€­`g“.ŸHY¦Ÿíà¸3îÝ÷e·‘ÿ=j¿uAßìõoä•/d•à>Ý ˜~HÔØñ»Ù¸g?È¬åÖ%¨ÂEÕhÄ–ó£ô¡æ†(•rÃ¡íCþ©ûì`‡Yóº¦œ!õ¼7E>æ ß:Î:­†€¾za;Å-¹â5ûò.ÔVƒQ?	£þAvhËv’º;‡ì«AÙ9¥EkšµöÃKí?k•zªrvº„i±”êr-ä|1ÐëQÂ?/Ùï0Å‚»\ÚkwÂ~%0Ï¤]Q—òž I¨`V´“uÇ¾)Z.ç½YPÊò#\Ë>½ÎOXÇÞ-W‰Ï©¬sËÒ–’FÕÇÞ®Ì"”%üÄTÌÈCÌLb:lÊ³ejË¾ú+1WKÙlFë½(ò5»4ËçÇ”Q¢¾øí÷—‡ÄÍÐ~¯æðëp3§±	»ÐSi5'èðw\q§µÂÃ»CDBo?"lÂÖC7*H[	­\YÍ`Ã˜ ƒ9×0ï´‚™Š_Iˆëã´-àïÂøJI”Ë¼B	`i½ ƒ/Ÿ“üi9¤U™ÜÆí.Ð59¡ô"‹QÌýD3­þÊÕÜh_·ÈÜÂ6ª@ÄñyˆÇñù)›xçUo—Ò­ft×yM9Õú¯wo(ã?:Í>Í}Ä¤gë**&Ý‚¶Ë­ -Ù*™Iü+g©î•à²ZÚÌ,TSäÎSÔq_¹NÆN)áTÄ` rÇ€èÚwlÇ…ŠþOF‚?}¯ÈmÜ¬Ö‚ÜzÈäPN³h,Q'ÝÝùÁšè/%œp±æÁ´óAWWð WW_Ô!óûîí÷¿üøaä¨±^¨¢·ëî	AqŒ“t¼7ª‹…ÂxYH,ª<eUü¿q€ñÏÞB•¶Ñ˜m“QM‰qžge~~eš<hf••sÍÆ†}ƒ¯Ôßò&ŽêÊo…Á…v¾\Ÿ_…NâqÌ Ýuèbc7-µ¤Ÿ’^´ŽÜ¤»}"§ˆ_5»ê´â¸	³_äc;¬MÕ¤,~|kºÎ(ÜZ7KOÛM•…=§éÿ¥0	W›úMRßâAë½¡+<
ü…un[n¯ö¥*·©wØðˆM›6‡›qY¸þ¦¸B!øN*¨É9¬hõö#fMáÝQºKð®)k6“…ð«±)ig]ç¸I2îÛmYPÌ§Ç×Nö4¹xá=¸÷©iüÑB|åBƒW¸k¢A{Ër.ÉáÇúÞDôäžEWM©y”Ñù&8ú^ª0¾JÙÆÙwû„¬‹ï+pû‘NcnéBí¦–(†â%ír‹F´k–€Õ3gW•ªÆnË7wÝ°Ð¤WA©à¡qJX6Jä”ª©¬/‹È÷’qÌ¨q‚®k*@Vlhd3Š-“	[sí¨qÎ¾Cíb·§èoÝT˜óy^:´Æõ³««)2ôè&
{RöòÞwä9íö——²îç½À­¤slM•b?m²òï@‘žj\“¤¬©³n2·
†iêÐsGž·ïhKn	üRõä´YÂRˆšöU½‰¼m~Å-½žqÜÞAßÆ©þŒV}V%Z¿Éž„a@z»9à}ÓÑÁÿ4£pÀß]ûN%ï!Ý[pªÔ4&¶A0CPÏ©ï¡ƒºh)¹{±ŒŸ6"|Ž·š#}”…¥q¼¿ø9D¤¸©ÒŽÄ•’ÔuQ“¶	ˆ•>ª÷ãŸÅ)’J].½Ñæ—ÂÅ;ÂŸ¨2^,=Kýêï÷n!½V>K£Žq½]«SÃ‰Ì©¤mÍr(õ®ñ9f'–Vj*WyÑ£jè9q;Ž`…Úø}ÂÔÁ‘T.¡_»ûíÎœÄå1GºC=¿rÑ>H@~¦¥ï#×"£v·°½Ós_wy«	{«µÒís´Ç¤uSÛÄ……ì¤‡ÇŽGŽ”±z÷GÏ¬Ð‡ o]Œ‰ËºG0¼íŒ1]Ê½‡ÙÝÇèÜnr[‹ÝævœÝOÈÔÂ¶ÄCÌ,ö7ì2±«‡™×C¡j1ØÇ¬Ôñ•OÊªÚ½O6*UïeSP2©á¸¨)5B+—CiöyK³[»‘}qª´ÏZ¥íã½ê´üŠŸ§`ÛÏ?É?Kí¶§~Š¥ÁaË¸ýŒoâ õ ÛA+º=î^°ÅÝ‹»}Mmÿªa¨ó\çímg»0K2ÐË Áã{°ç0ˆ­Ò
SóLŒ wWP;Z|œ‡^%!} p,…ß±åY&€‰nªªÿ8=g'‰¦OÚrá¢äÔ¿®é	ËíÍøo´Ìß¼øÆõ3¿M&“ðž/úÛwµªRZ÷–Œ“¦KíjM}ß+Ó—æžÓ	¯ÀEfc¯´ÙnMi)íì{øsPþ	{îÅ—¼9(oœŽ÷âÌ-LIÄÝ‡3eÊY±Ã6¡Á|Qõ@—ÓEÕO-		îLÔ^~©×Ÿƒ)-ìâ¾´/JO¤½;D§ô4@.Üëß¶ŸeæùØ¿@È?ú mŒµ]mcôÒTÿ:ÖßÏ‰/Ïœà,èßã¯Øc‚­¸ÎŠÆÈ•A[s¸"Í+ëžç{éPûÛmïaVá­T‰¬šÛ…+•ÜÛª|?ìBáXIGä„½½†HêyMDn¿ 5¼¨¶{°ÄLý'ºÆá^á‘u÷[s×Üˆ*05+K¨N­Ýþ5œÖ¿5,vÉ‘{8~*h`¤(KûÐ¤é÷DÜTa¢þ%Ÿ){uoÇüøÒÞÔ‡ß|}ïGÞÙÛ‰Øã¹–Áþ)Üèÿ„4(a     0707010005c822000081b40000000000000000000000015ab2175400000dbb000000660000000000000000000000000000003c00000000root/usr/share/doc/opensvc/template.service.disk.vg.conf.gz   ‹      í]mÛ6þž_A (œàlg7×~Ù6Áíµ).¸¶[ Iï€¢ðÒm–D•¤ìuq?þf†¤DÙë—Í®·ëBÚ®-iDÎóÌ9RŸ?ÈÏ³çì?(.•fÞgvU
¶˜Þ_Ü~@ÜÃêîÙ¯8Ùçg¿=ûšæûfüìŽz.VK¥Ó‹ææÏ<àµcUòq&ê;}ÔÞ‡iñ{%µH7”Z-¤‘ªÅ”~Ï3CGR1áUf›Aÿ¤
: ‹™ÐÒò"ñ÷ÉŸ°·l&x!ÌWhwÜ”"‘™À9SQÀµ	iî`8ããL>˜š0/TVÁ·©VU	'~MÇÞ°‰RÛ´™šÉc(³VÍ†6ë#wPg@àAÔÉXÂ‹T¦Ü
sÑÈgÿk¨b!t4€±Rp«¢…Å¿~|ú/SE¶²¨µÇòÊX6ÌË¬bXhöÃ/?â|-HDæ™ÃLeÉÍ”½JÅâUjæ¯†Ã!Üf!a†ì
Žé¥üCôO3•Ä×D’ÜÕrír¤ŒXAZÜBUZ@Âü©v¶á’bËÆÓD HÐK@¯*UÎ9!i8œ÷†mÓp¹xín7¼­nl«véÅ2	¶^YLêEwVÎVF&<ó~ÍÜææHÝ ÌÝn®ÔðÛeóã‡ïÄD NÞœX
m QXºzÁ‘À¨IòX¨uø]U:ÃÈÇ|äsSj©`|+¦À™Ñ¹(ÜÍ ÷–áÄà~©C·Ås")…JÅœì$Œ'œëÀ¥«vÂ[¨Q©…ÈK;âc¥íÉ!]yR!íý$Ä®ž…ë{}vUŠâÃ/ß²¥Ì2æUc2²Åš¥´3Æ#€Ã™‰Ês„_@5ž¢=óú  Á!û ÀÄI´åz*l$Næe&rà*w^6UÀ GU–p)K–qp(9‹½ææå°™L$¨G1¹út„í³Þ:™z¨MŒã<±fä„9r4™8pó`c–DÌ]—$v lóRÓQRVO5ê~¦ŸºkSôJM#ØEÝQ9ÑìWóo-‚^f™Zgƒ#†‡¢ÇX)åbÎÕ^Õ;dßÂ¿×±jNáÆ#Í4/¦‚]°³þyÿ5þp6xMÉápâÛÁÌEÞy<0A½J¯(z˜8(ýýŽ0ß\Äm/¸À€‘™øâÝßldaÅTèüÿºp¹–-x9äÙiŸ-g2™ÁßÏ€ðóùÙë/™Ó?øö¢n šÁXUÀ#mEA¢ÏDa*àB”@Ü ‚ ãÇ\8ƒGÁÕKkbÏ€÷ÀHRÑÉû$ˆ²Vˆ0 Œ	D#™‰ Œ59Â‡#Á„@i…5\‡-Jû¾:ßÇ¾ß+eyG¾Cýæ‘ãR ¨0 '"¯Ÿ«¼P~xDu“öEœƒ˜Æ­ ¹°´ÇkøØ@q2A&0 qzÄ5Ï…zÈ¾G$n8&8}$Ñ²¨nXBW˜>œƒ×ãòTE&siñÞ”3ÅÒ°^.6Æâ.ÄqU>úÁôs™hehjbDâÃ½I•ÙWg_üÎñ²à¥pðêÐ¸±à9¯[gDÒìR¹scÎ‘‘òNúîˆ:R*Á€­VYGþä?Ðó^²IÆ§ìÅFÁó—è-¸KÔ‚ÁŸRiÜßÈ«ŠêtcçP ´Z>‹Üó•rn''é/ûÉ›¹qò¹µ”õ[ä¸æÜyàx%ÌÝ‚\2ÜyE¡<øPü[æ¹H%L;óIÙØåWW?F‚Ü Ç†#þ;Zp\â¨—äV~ Õ˜À3Ffª±YA!“³ðZ‚èÑ
°ì|Øä1kéç{8NÆÝ±û®ì6òÑ¢ö;
úæ¨¿‘W¾E„ûxÅôKO¢Ê®&ƒ–ÀøÌZNÁºV¸P5±æü0}(¹1a™0FÛ…ü3úìa‡Yò²Äœ.õ¼3Ens€ :Î­ú€¾Za;Æ-ºâûrj‹Î¨Ÿ„Qÿ!;¶e“¤æÎ>ûò›ASšµvÃ‹í?k{¬röº„q6—j´r:ëèõ áŸÏÙû+˜bÆ)—vÚ²_Ì Ò.…( JyÏ ¡¯`#VÔ“¥ãß-Êy·!”²ü×²Ï¶ÂùëØ‡ E•øË:Z–¶˜4ª >pìõÊ,ä‘·‘Æb‚bÉ$L‡y2mÙU9ÌÕb6›àú¡D¾a[A³|zzAJÔ¿þöò˜¸\ÀoÕaÇ56d—z,­æ|+î¸öƒx8wèðCh­ã„ßzhFÒüVB-W°a˜ Áœj0ï¸‚‹_H×*Æq[ÀÝ¿ÇøBI(—y%€Åõ‚|ù™äÎˆË!­òè6´»€×T¦‡éE¢ý„3-^q¢öMÌ¶a"NÏC<ŒÏÙwZ´v)i5£¹ÎiŠTëþÜ½¡èÿä4û4÷/!=[A©`Ò5HÞaSnpá’=@%	ÿ•“Ø·JpYÌÚÌÌT•¥ä)Ê°¯ÜGcÇ”p,B0)1 ¸ö=Ûq¾¢ÿ‹‘àÏ@ß)rwÀ¬–nÝgò”‡ÓÌ*‹Ô‰wwÞ[ü¥'œ-ùÊ€`ÜùA××àA¯¯‡®¨ƒÌï»wß_~úácŸ¨±œ©¬µëçï	‚Â‡ñx7ª‹™‚ñ2ŸXq·•±ªôþßP`DøÚZ¨ÒÂVf[%XSÂ8/’<½¸6U4³Hò©fÃ¾?±ïmÕµÛ
fØÅ|yqíg8Çañ®C›iQÔ’nJ¼p¹HsûHN3·jvÝhÅý°	³ï6óc{¬ÍµLñÓ[Ó%£ µnæŽ¶«"ñ{OÓ!ÿ¬ `"&T›ºMR×âë½¾+xäùÖ¹n¹­Ú«\j…ÛeÃ}6®ênÂeF5ü¦¸LA0p XP£sXàêí'#&UFŠn(É9ð®ÊK6‘™p«±1i'MõåñÌ·?îm[Ö(¦ãÓk'{š\¼tÜùÔ8~÷q!¾ ÐàNmC8hç`YÊDrðcmoDtäžW©yÑø&pô­TÁuê¶\º»„º}|Ö‹ë+ }@Ò-ý€P»*©iŠ—¸?ˆp×XÖVOÈ®
UhË7¥~Ð¤SA®ÀCÃ)~Ù(’“«ª°®,Bß‹Æ1ÁÆ	¼®* ²l…#›`lÙ’k¢Æûj»>EwëÆ üœ/üðâ¡mSÐÏ¾®¦ÀÐ“3˜'Ùgþ!î&Çýå¹,Ûy/àVO’[UÄ„O¬üÇS¤¥j²“˜5@ê¬«„VÁ`šÚ÷Ü¡çm;Úœ[2OEKN%Ì…(qoP•«ÐÈ[çWÜrßëÆ½»?½=£ŽUÃªHë›ì‰ÈpÄ÷ØÍç›Nþ§…=þtí8•P¼ûtoÆ±RÓ0±3ê)öÝ@èÀ.ZLî>,Ã§ŽH}—ã-¦>ÊÌâ8>\þä#RØT©GB¥$vg]fÙmÒü6²ÒEõvüsAq
¥bW¥¢í`nÁ_¼'ü‰"áeÇÒc°Ô­¸~ïBßkå²4ì¸7ëµ:6œÈ[@êÖ,B©uË1±¸R[ r‘f-ªúžÚ ‚ejåö	c‡R¹}¸¢˜î·?s£SŽtÇz~å²~ 3üDK×G$nD‚ín~{§å¾vy«!{§µÒõs¸Ç¤uUÚÈ…ùì¤…ÇžGŽ”±zwGO¬ÐÇ o}Œ‰QÝÞzÆ/åÞÁìîbtn÷7¹‡€­…Å~s;Ín‡'dj~[â>fúö™X‡ÕýÌë¾PÕbVêôÊ'eUõÆÞg•*²©¨{™Ô½p
ìjŒ •QWš=ni¶¸mˆŠcW¥=j•v ŽwªÓZðw(>NÁv˜1~–)v>Jív ~Žv¥ÁqË¸ÃŒï€â uÛQ+ºîN°uÅÝ‘‹»CMíðª¡«óŽ\çlgû0‹2Ð‘×àé=ØsÄ–3i…)y"Fà»+°-<Îƒ¯’õKsávly’ÀDWEÑ~œž³^¤é^].\6‚èAý›Ÿ°\ßŒÿFËôí‹o¨Ÿùm8ú÷|áw×Õªrié-½ªì‹íjUÙï•iK£çtük à†"±¡WÚ¬·¦Ô”Øõ†ÇŽ?ÂŸaÏ¸Cy@Ç›£ò†t|g¶0%wÎÜÊ”Y¡Ã6¢ÁN¾¨²£Ë‘é¢Ê§Î–ˆ»¶—ôò1˜ò°¯þ~š}i{^”êŸH÷z'DÇø4€'÷ú³Ì<¸¹G´ñÃ£¶¯m_šê^ÇÚáû˜øò„ Îÿ'ƒ¯Ø‚1ÂVÜ$YeäBôA[SpEš–žç{I¨ý}Û{˜•+U$ß”M¥½­ÊõÃÎDæßuDÙ»‰=¯‘Èõ úÕ6–PÁÔ~¢kàßéåY§Ø’Ssw$*ƒ©Y™sOulív¯á´î­ÁÀbJŽèáø±À¡¢,>ìƒ/Æß#qcu/ùŒ¸íÕ½óÃK{c¾ùúÞ[ÞÙÛˆ8à¹–Îþ%ÜèÿVþÒó
f   0707010005c823000081b40000000000000000000000015ab2175400000d1a000000660000000000000000000000000000003e00000000root/usr/share/doc/opensvc/template.service.disk.vmdg.conf.gz ‹      í\ïoÛ6þÞ¿‚@1¤ÅÙ¾¤·}ÉÖâr[·­´Û}‡–h›ˆ$êHÊŽ÷Çßóò‡D9Mì4q2°Õ±¤—äû<ï/’âóçùyöœ=â‡ÄåÒ\˜ÝÔ‚­Ê|ñPqø¸ÇÕÝ³ßh°ÏOöµïk7âgÏ¨ßWb³V:?ïšÏTe¹¬„žÊ7ŒñCz2™ªù¬m‹t#è‚ÿi¤ùµV+i¤ªdµp¿ç…qWr1çMa»Îÿ¬*wAVK¡¥åUÚ)Ÿ³7l)x;!Æ-´¿nj‘É¹ÌpÏB`ì2sÚA&Ã–‚Éœ©9³øÖjˆ­—Ê¸¿çrÑhnÑIf×VóÌº›Iõ	?J^×›kUNÐÆ×©ºÌ\©Û€©5~ûCiÿ ùNÌ¡5Ãxwc-´‘ÆŠÊ¢ëFè•ZƒZµC¿«Fgb’@ò_AN­¥Bÿ6L­„¿—$ÈLŒ±íåÆ«œîI¤T*bBìO¼×¡ìŸºÞJMk-DYÛ)Ÿ)méöÊ£@G%Ëœ[aÎ»Þ²ÿuí@ÛÀ*éÁL)4Uõxò –¬òÄâù“{W‹êý¯ß²µ,
TŽ>ÙcÍZÚ%ã	ÀñÎL•%Á/+P;§ÀÛ‹tÁýGNØ{U
/Úr½6'Ëº%¸êš3,W` úÑÔ5¹¤a7–• {!ÌõËI7˜DÐÉœôq}zÂŽØÉ6™NH›l&Ü’ÄANŒ‘“ÉŒYÑ<ØŒÃ’s·%Ä€Û¼ÔbšÕ9:öÞé§îÁÚ\€^¹égZ@ßÓzÑì7êóï=‚^…ZGgC=Î„1Ä×™¬¥bã½QëUH½ö-þÏ¸N]Pwwà	‰šWÁÎÙéèlôŠ~8¿rÉã8é‡ÛÁ,E9€y80¡^¥7.z˜4(ý”üN0ß\Âm'¸`ÀÔ,!~€ønˆ?ldeÅBèüÿºò¹u–­xˆGÞ‘-Êl‰ï/Àü|vúêKæõß^õÂ ÏTÖi$FLT¦’ìq!Ç6+ÜU¸ziMê¨ŠTJN>$A.¯ED@‡(dˆF²Q%!ZrÄ'‚ƒ„ ´B7tì‡=J}_íbßeù@¾}ýb®Þ PÑÐ“WŸ"è;Ñ$º …VMÍäœ}‘æ ¦s+ Fëžá3£Š2<!‰’tè†À4=âš—Â
=aßÃ‰kN	ÎˆHô£¬šk–¹'ÌˆÏàõ8Rž¦*d)-µír¦TšëåêF_üƒÔª)g †_ÊL£ƒæ&UA"Npô&U>a_~ñwÜdá/—ÂáÒ¡ñ}¡{^õîH¤Ùµò÷¦œwD&Ê{éwGÔ©Rå”*?­Šüw’OÏ{Áæ_°§Ï^’´p—¤C?¡÷ß‰;ïK˜‡{…:€¤µò©Xä¯.çörrH9B&o®Œ—Ï­uY¿%>À5—Þ§å¾oÂ¹d´¼q¡<úPú.ËRäÃ.BGr6ó9Â»w?%‚|'½ÇÆ•ð7Ypì‹qêCÍŠvý€jJà¥ˆ=3ÍÌlPÈ”_Ó(‚–=­€­`g“.ŸHY¦Ÿíà¸3îÝ÷e·‘ÿ=j¿uAßìõoä•/d•à>Û ˜~HÔØñ»ù¸gè?È¬åÖ%¨ÂEÕhÄ–ó£ô¡æ†(•rÃ¡íCþ©ûì`‡Y»™0(r_Š|Ì¾tœuZ3N|õÂvŠ[òÄköå]¨­£~Fý‚ìÐ–í$u-‡ì«AÙ9 Zûî¥ö‰µJ=U9;]Â¬¸’jºr±èõ(áÈ‚¯Øï0Ä‚»\ÚkwÂ~%0Ï¤]Q—òž I¨`V´ƒu×¾)Z.ç½YPÊò#œË>½ÎO˜ÇÞ-W‰/¨¬sÓÒ–’FÕÇÞÎÌ"”%üÄLÌÉCÌ‘LÒzÒŒgé2S¨þJŒÕR6›ÑüC/Š|ÍnÍòÅñe”¨/~ûýå!q34ß«9ü<ÜÜilÂ.ôLZÍ	:ügÜiî‡ððîÐ#ºÐ›Ç›°ôÐõ
ÒÂRB+WVsØ0è×5Ì;­`fbÉWâzÅ8-øöO_)‰r™W(,Ídðåb’¿#-‡´*“fÜê=Ó˜J/²ÅÜO4Òê¯ÜQÍõöu‹Ì-l£
DŸ‡xŸŸ²	wQõV)ÝlF÷œ×”S­ÿz÷‚b0þ£ÓìÓ\G¼@z¶®¢RaÒ-HÁa»Ü
pÑ”= ’™Ä¿ržúá^	.«+ƒB›™¥jŠÜyŠ:®+wÂÉØ)%œ‰Dî]ûŽå¸PÑÿÉHðG ï¹;pÕ:B[™¼*Ài–%ê¤«;?Xý¥„.Ö|c ˜V~!èòôòrâ‹:d~ß½ýþâ—?Œ5ÖKUôVýB›û8Iû{£ºX*ô—…Ä¢ÊSQVÕÁÿWÿìMTiaÑ6Õ”èçyVæç—¦ÉÓ€fVY¹ÐllØ7øZñR¼‰½ºôKapa†_­Ï/Ã'ñ:F®:t±±–‹ZÒÉ/šGî:Ò5ŸÈé:âgÍ.;­ønÂì'yãÄØkS5)‹ßœ®3
7WÀÍ•§í¦ÊÂšÀÓtÈÿR˜„‰«Mý"©ßâAó½aWxøëÜ¶Ü^íKUnSï°á›5m7ç²p5üMq…B0ð;A¨ &ç°¢ÙÛ_Œ˜7…StGéB^wMY³¹,„ŸMI;ï28ÇÅH2qØnËº€b>;¾ídO“‹Þƒ{ŸšÆïMÄW.4x…»mCÔiï`YÎ"9üXßÛˆžÜóèª)52:ßGßKfÂW)Û8ûÝ>!kDÁâ÷¸uH§17õ¡vSËCñ’îr“F´j–€ÕsgW•ªÆnÉ7w»¡I¯‚RÁCã–0m”È)USY_‘ï%ã˜ÓÆ	z®© Y±q›$)¶L&lÍµ£Æ9ûµ‹Ý¢oº3¨0æóÐ½´k7Œ)êg×®¦ÈÐ£3˜(ìIÙË{¿#Ïùh·¾|%ë~ÞÜÚA:ÇÖT)ñÓ&+ÿé©Æm²“”5 uÖMæfÁ0LöÜ‘çí;Ú’[g¿T=9m–p%DMkƒªÞÄÝÀm~Å-{=c¿½ƒ¾Sý¬ú<¬J´~“=	Â€´»9à}ÓÑÁÿ4£pÀß=ûN%ï!Ý[rªÔ4¶A0CPÏißBí¢¥äî}Ä2~Úˆ4ò9ÞjôQ–úñþâç‘â¢JÛWJÒî¬‹¢ø˜´°L@¬ôQ½ÿ¼@(N‘TÚáRÑÛÁü4BxxGøUÆë¥‡`©Ÿ=ðû½[Ã^+Ÿ¥ÑŽq½]«Ó†™Óvk–C©÷ŒÏ1;±4SÛP¹Ê‹UÃž·áV¨_'L\û®Š/Š]{»3'1=æHw¨÷W.Ú	(ÃÏ´ôûˆÄµÈh»[XÞé¹¯»¼Õ„½ÕZéö=ZcÒº©mâÂBvÒÃcÇ+GÊØ½û£ÇçVèC€·…Ç®ÆÄt€îo;cL§rïav÷1º·‡›ÜcÀÖÃb·¹çn‡'djaYâ!f÷7ì2±«‡™×C¡j1ØÇ¬Ôñ•OÊªÚ…½O6*UïeSP2©á¸¨m„V¦CiöyK³[»‘}qª´ÏZ¥íã½ê´üŠŸ§`ÛÏ?É?Kí¶§~Š¥ÁaË¸ýŒoâ õ ÛA+º=î^°ÅÝ‹»}Mmÿªa¨ó\çímg»0K2ÐiÐàñ½ØsÄÖKi…©y&ÆFÐÙ´-¾ÎCGIH¿ 8–Â¯Øò,ÀD7UÕž³“DÓ'm¹pÑ	r/ê_×ô†åöbü7Zæo^|ãö3¿M&“pÎýíwµªRZwJÆISŒ¥íjM}Ï•éKsïé„c Ð ÈlÜ+m¶·¦´” ‰vî{øsPþ	{îÅ—¼9(oœŽ÷âÌ-LIÄÝ‡3eÊYq‡mBƒ;ù¢ê.¦‹ªŸ:[Ü˜h{ùT¯?SZØÿÄûÒv”ÞHzwˆÎèm€\¸ãß¶ßeæùØ ä_}Ð6Æ€Ú®mcthª?ŽuÀ÷sâË3§ 8:*|ü{á`L°×YÑ¹#hkW¤yeÝû|/j»ífN¥JdÕÜ.]©äN«òûa—¢g`%;"'ìí5DÒž×Däö¨á ÚîÅW0õßè‡3½Â+ëî¶ænsw"ªÀÐ¬,y :míöÇpZj0Xì’#÷rüLPÇHQ–^ö¡¤é÷DÜLa þÏˆÝÛ1?Ú›úð›Ç÷~äÌÞNÄïµnôOáFÿ¢äaþpa    0707010005c824000081b40000000000000000000000015ab2175400000d69000000660000000000000000000000000000003f00000000root/usr/share/doc/opensvc/template.service.disk.zpool.conf.gz    ‹      í\mÛ6þž_A (6ÁÙ¾Ý\óeÛ·×¦@qms@ÒÞ‡"ðÒe–D•¤ìup?þf†¤DÙë—Í®·ëBÚx%kDÎóÌ9Òóçùyöœ=àÅ¥ÒÌÌ®*Á>WJå÷÷€÷°º{ö;Nöùù§gßÐ|ß¸?{†Ÿ‹ÕRéô²½É'†øA™DU|’‹æNu÷aZüQK-Ú!üÀsCg*­ÒHUÊrzÙ9“ŠŒ×¹mGý‹*é„,gBKËËÄß(<coÙLð4ŒB0˜°Ðî¼©D"3™Ào¦¢„kRÜÁ$ð‹3A
a*c¾Î#å=öÃ2¥¶©ò³U¯Ê JTˆÓ')“O‹LhÃ¬±3f„D'¥a<±rÁ­HaD‘¥Ù .Q¥7v„Ð,R±øS!	'6	'ŽC}-„ŽäçÒØ¤POtf ÷Mò:…a2UY0ÏYÅa « ^3`Ü°¥ªó”MD„QÅÜ¬òh&Z „Éß‰T¥áXo=“ïE&KÖÐþ°³€DiaèFh0"jÔªÉ@à¸ªu‚*oFø‘ÏAN¥¥"~‹d"†¹Xˆ'÷K
Œ—)þ&’RªTŒ€JYOø-¡ë®Ú	o©Æ•¢¨ì˜O”¶'‡tsæ¡ì”,S°sÙŽ–ý¯½Ï†áNÀ¦/;<q.MîÌÂõgö¾å‡ß¾cK™ƒá:•Ã˜Œì°f)íŒñØvý/U¿,j<Å@È›“x‚þCGìƒÏN¢-×Sa#q²¨rQ Wév†¥
 ã¨«
.r§Ï¹±¬ b±ÂÜ¼µ“‰e¨3Ð§€ñâlLg¨MðEQð€9r4™a$+˜›p°$bîº$ ±`›—šŽ“ª6'ÇÞ~ê¬MÐ+5­`çãÇÕD³ßqÌŸ:½ÊsµÎGœƒÑŸMd‰(å+ç¯‚ê±ïàÿŒëØµ?èƒ¡
9¡y9ì’.¯ðÀùðy$‡À‰¶ƒYˆ¢óx`‚z•^Qô0qPú9:Ž0ß\Äm/¸À€±™øâÝßl$dÙS¡;ðÿ[èÒå
8X¶à9D,pä5d}¶œÉdßkžàðÅù«¯™Ó?øö²n šáDÕÀ#mMAbÀDij-âìâ?ÜbW/­‰=Þ#HE'ï“ Jl!"Ô˜Á
8RZ™‹ Œ9Â‡#Á]
«`:ŒÃ‹¥€}¯/ö±ïZYÞ“ïPÿ‚€y¤Àøª+úyEø$ ¿#M‚šjUWLfì«81­[rÁlé>1*¯A†ã/HÂ¤´!°SÚh(c­Ð#ö8"qÃ1Á ‰~’e}ÃºJ£áx=)O]æ²ïM9S,Mëåbc,îBGY L¿‰VF€¦&VA$Np0<Ð›Téˆ½>ÿêŸð/þ¢ þæUçq¥½Tî·1ç‰ÈHy'}wD+UŒ±N×¸rÒ“ùô¼W,Ëù”½8Ç(xñ w‰Z0x(•Æ}Gî¼¯-bîcìê ”ÖÈÇb‘{¾RÎíä¤ ýå 2y37N>·–²~‹| ×\8‰òá\2ÜyE¡<øPü.‹B¤¦û¤lâr„÷ïŽ¹A:güßhÁappYˆ; >¨Yá¾n Õµ¥"?2SOÌ

™âœ…×DˆV€­`£6ŸˆYL¿ØÃq2îžÝwe·‘ŸE‡Úï(è›C¢þF^ù¢³D8YA1ýÒ“¨¶Ã÷Ù°c0~ ³–S°.Z^„ªÑˆ5ç‡é®c*cnÚ.äŸÓg;Ì’W®àô©ç)r›ü ÐqÖjÕÌ°ðÕ	Û1nÑoØ×»P[ôFý$ŒúoÙ±-›$µwöÙWeçD ‰pÖÚ/¶ÿ@¬Eì°ÊÙë&ù\ªñRÈé¬§×ƒ8„A<g?¾‡)æœri§ÝûÁ¼ íRˆð¡”÷ ù
6bE3Y:ïñÑ¢œw²@)ËOp-û|+œ°¿tZT‰O±¬£ei‹I£
êÇÞ¬ÌB)ð‘¡‡È ™Ä¨	Oæ±-»ê¯€¹ZÌf\? ô‚È7l+h–OO/(C‰úâ÷O/‰›v­æpëpilÄ®ôDZÍ:ø;¬¸×~ÇÏ¹C‡„Bg? lüÖC;*æ·¹²ÌÀ†a‚sªÁ¼ã
f"f|!A\§Çmwÿ3ÆJB¹ÌK(,®$àË§È$÷‹¸ÒªˆnC»xMmÎ0½HB£C8Óòïœ¨F£}Ó ³…mXˆÓóãóc6Aà–]JZÍh¯sš"Õº¯»7½ñŸœfŸæ>â¤gË2(LºÉ;lÊ­ .\²¨d"á_™Å~¸S‚Ërn ÐffæûTê~_¹ŽÆŽ)áD„` Rb@pí{¶ã|Eÿ#ÁŸ¾Sä:î€X-ÝºÏä	(§™Õ©ïîühMð—ØÐ“/ùÊ€`ÜùA××àA¯¯G®¨ƒÌïûw?\ýúÓG×´œ©¼³ëçï	‚ÂGñx7ª‹™‚ñ2ŸX”i,ÊªÊûC€áÏÎB•¶Ö0Û:ÁšÆy™éåµ©Ó8 ™ERL5ö-|Å&§·aT×n+\˜a—óååµŸá(œ‡Ä»mll§EQKº)QðÂuäv íí#9í@ÜªÙu«w`f·ÈÆöX[è:9s#£ µnæŽ¶«2ñ{OÓ!ÿGAÀDL¨6u›¤®Å×{}WðÈó¬sÝr;µ/V¹uµÇ†lR79\ÆeN5ü¦¸\A0p ¾å»ŠFìW#²:'E·”ÎåxWËd.ÜjlLÚ¬Íàˆ‹MÓÙ›C`Û–uŠéäôÚÉž&¯œw>5Žß\ˆ/)48…SÛÚ9X–r‘üX×Û¹³àª152ZßŽ¾“*L„«RÖqvÝ>>k„‚ÅõÐ¾@ iŒ–~@¨]U2ŠAñ÷Ñ¢îËÎ€ÕÙU©Ê!mù¦ÔEšt*(xhø‰_6Šäª.­+‹Ð÷¢qdØ8×Õ%@–¯pdÆ–Ñˆ-¹&j\²ï¡v±ëSt·nÊÏùÒ/Ú†1ýìëj
=9ƒ	Âž”½|pyä£iy.«nÞ¸5“$ÇV—1áÓ$+ÿõé¨†šì$f:ë:¡U0˜¦ö=wèy»Ž¶à–Là×²#§ÉæBT¸7¨ªUè€oò+n¹ïõãvz§º3êYõ8¬Š´¾Éžˆ÷a@|Ýp¾éäàšQØãO×>€S	Å»O÷f+5[A0ƒ žbß„ì¢ÅäîCÀ2|šˆ4p9Þb
é£Ì-ŽãÃÕ/>"…M•f$TJbwÖUžß&ÍoÐóÕ»ñÏ	Å)”Š]”Šn´ƒ¹eñžð'Ê„W=KÁR·zàú½}¯•ËÒüó<kµ:6œÈ[@šÖ,B©sË1[±¸R[ r™æªúžÚ ‚åjåö	c‡R¹}¸¢˜î·?sãSŽtÇz~åªy 3üDK×G$nD‚ín~{§ã¾vy«{§µÒÍs¸Ç¤u]ÙÈ…ùì¤ƒÇžGŽ”±=zwGgVèc€·†Ç¾ÆÄ¸‡îo=cŒ—rï`vw1º·û›ÜCÀÖÁb¿¹f·Ã25¿-q3ýûL¬Çê~æu_¨1+uzåÇ“²ªfcï‹JUÙTÔ½Lê^8v5ÁÆ@ÐÊ¸/Í·4Û
Ü6DÅ±¯ÒµJ; Ç;Õiø{§`;Ì¿È{¥v;Ð¿ÄûÒà¸eÜaÆw@qÐ…º‡í¨Ýw'ØúâîÈÅÝ¡¦vxÕÐ×yG®ó¶³}˜EèØkðôì9bË™´ÂT<C#ðÝØŽçÁWIH× 8ÂíØò$€‰®Ë²û8=gg‘¦ÏšráªDêßTø„åúfü·Z¦o_|KýÌo£ÑÈ¿çÿv]­ª–Þ’qVWc±]­®ÎÂ{eºÒè9ÿ ¸¡Hlè•6ë­)%€D{ûÞzþ•?'Âž;q‡ò€ž7Gåéø ÎlaJ$î.œ¹•)²B‡mDƒ|QUO—#ÓEUO-	v&l/ëåc0¥ý/Ü—¶çE©þ‰t¯wBt‚O¤‚^ÿ¶þ,3O‡îBîÑmCüð¨íkÃ—¦º×±öø>&¾<!€³ÀWã_³c„­¸IòÚÈ…€¶¦àŠ4/-=Ï÷’PûÇ¶÷0+ÿVªHVÅíŒJ%z[•ë‡‰Ü¿+êˆ±w7 {^#‘ë/@õ/ªm,¡‚©ûD×Ð¿ÓË?²NØ’Ssw$*‡©YYpOulív¯á´î­ÁÀbJŽèáø‰À¡¢,>ìƒ/Æã‘¸‰‚‰º—|Æ@ÜöêÞ–ùá¥½±ß|}ï-ïìmEð\KïFÿnôÿDýL`d     0707010005c825000081b40000000000000000000000015ab2175400000ead000000660000000000000000000000000000003b00000000root/usr/share/doc/opensvc/template.service.fs.bfs.conf.gz    ‹      í]ksÛ6ýž_™LÆÎ¬¤µ³Íç1õn“Ì¶MgÝf?t:6D‚Ö$Á du»ÿ}Ï½ )P~HŽ±»ÒL[$/Á{Î}—ÈÓ§·ùyòTÜâ‡Äev Ü¢RbœÙ[w‹ˆ»]Ý=ù9³O÷~yòŠŸ÷=ñ“'4èSµ˜›:=XÞ;©•têØTN›Òâ”á-~HM61•çª»çu£è@­~mt­–ƒy/sËGªÚÌ´Åxt99ˆ/IU&›Ü-‡ÿ½)ù€.§ªÖN–I¸O®d&ÞŠ©’i;%ðäªöÇm¥éçLT‰k:-1åLÕ‘|;ÍÕk÷¶	¦©&UÉ\	gD%-ÿë¦Jäf¢ž™¼)TP0D…,ÓQÄœ#ý›øN”çÉrœœ
«Î{òªp1B§'i¬X\élòcÖ€ƒ•k2†&ÕöTLjÓTük¦seÖ©Ìt¢€·iòTŒUS÷*Ym
†f6™1—a`íE¡=ð€l‰U²‚}Ù¢³b8dVíà¡êçha6‡J,HUµ²|Î]¾}öŸ÷ÿ|÷î÷>ýþéïÿeäøÄ+±&teÝ«s“œ¶Bê'ÝF¦£9	¦Æ:–ÐŒ ‰Î¦…iJ7ßè,Sµ*]+9‘%ìŽ|Ÿ€yÁ´=#UL46Ü„®ýš¾c#·‹ÒIòÖ¯ðõÕÿæõõå0¾ÜI~I!ÞU²c´1”O1m$>d„Ê =Â ŠÊhü­mN­*IxÁOâ˜†$·’n*jc\fÙù^WÂS0è_f±Æç¯:;Z	c¬^(o­v)§ØòÿœŠC¦Õª‘´´&Ì—²:þâ±þ!)óC¶§á$Ä¸ÑH«àÔ%«Œk5Ü#¨ô¼I)ž©|±¼`÷ßTÂÍÎèïÑhôÜß®‰cŽA¶Qk
ýèž\ìï=khlœÀšžWóÞnˆDÐñÁÑ#Î3Úƒ¸µÏÏ‘.úA†0ÎTÔˆDÉk/ú	€YÓÔ‰²«ÂšR³ ïÔDŽNyæu¤ºš{¥9F¶£ŠÊË±©Ÿ=wGn+ãD-¥S b–£¿/ïs.%ƒ[•«¦œØq¸~g >Vª<úô71×y.‚Ê1&«	XUÏ$9E”“½ºÁŸ*?<%‡ê$”ÝA:ÀÁ‘82”Çh'ë‰ŠS]T¹*^¥¯S`MUáRo¹´®,…­({ö|´|˜HÐNFúØ!ƒcÚwVÉ´Óæc22ËÙz+U{!ß3[Œ¥Õž½«’@bÀ%4®&ÇIÕ<Ô™‡Ït ×`mª@¯Ô.‡ª¿š@´ø™ÆüK"ÏÍ\ñ(mÆˆáXxòa¬KB	.“½˜¿€#õŽÄßð7M2Ä¾¬;E‚;Ü'jY"?{ƒýÁúboø‚p8Núâr0UlÁ¼;0¡^S/|!O&}}ÏsI×—p[.pl§¿…øjˆ/6ÈÞÕDÕ=øÿ¡êRå]Ž2“9"–¦
W¥äú:™âçFæà¾Þß{ñ•ðú‡o/{áÐÇÈbS¤-®á 1ª´M²šÀ±Ô€ äøqÄ
>
W¯]œ|ñ=(RA*9yïå9}¡ˆ€Q$H¨ì*¡Ñ‘£ýH"8HJ£nÇáƒER`ßËýuìûµ1NnÉ·©!ÀR0~‚@¥ú;1¾Œ$|š {ë‚ü,©ÎÄ³8±K·ráiù9¶Èá!Ãó’(éÀ€Î	ŒÓ#YËB9UÄ{8"u&)Á‰¾Ões&¾¹þp^O"åiÊ\ÚÑ½9gŠ¥°õÂêXü…4Ž²)Æ ¿ÐIm¬‚¦6VA$NIô¦M:/÷ž}s‚¬—¾Ž /H‡Ö…ÎyÑ;#’ææÆŸsž‰L”÷Ò¯Ž¨ÇÆÇ4÷_›|Kþ+É¿¡ç=Y.'bw¢à>Õ’Ò	_ÀYú*ÕÖÿLÜùØ8Â<ÄØSÔ$­“O“Ó2ð•sî¶ÜÝ{>@&oO­—/ã¬ŸN¸æÂ{àHTïì’qçEXò>”~ÖE¡RÇÎÃ@¸üäA~ü.äé=6Ž„ßÉ‚ÛÁá²6î@}
*[„gð¬1i†ÄÌ6c_:¿¢§ZBô@´¶Jì–ùDÌZ0}ÇÙ¸·ì¾.»Ï­«¼ã o7‰úçòÊ]]F¸óœÄó@¢Æ?fÃž`ü s­'°.žõEÕhÕŠó£ôÖG¡Ê˜Œ¶ù{üYÃ;—U¥KÛRäº¹È:)–ZÓ/´­„í·èŠ7â««P›múAõŸ²»¶l¿šÛÝ9d_ÊÎ±‰è©k?¼Øþ[bÍb@UÎZ—0ÎOµ9ž+=™néu+á¯¼Îûá#1ç¹wáµ;ŸÌ éæJ•À‡SÞ} 4
lÄŠîaùxÀ7F‹sÞË¥œ|„sÙ{—Ây¡Å•ø„Ê:ž–v”4šV}pìÝÌ,òH]ÀOŒUF"C2IË%c™œÆ¶ì«¿Ïê(›Mhþ€ÑkE¾—‚æääñe”¨»?ÿòü.q³4ß«9ü<\Æ‰Ãz¬]-	:üÞÎ¸ÓÜááÝ¡G"¡7ß­*…¥‡å¨ -,%tru™Á†ñ€¾m¢†yÇÌXMåLC\¯§eÿ!gF£\–%J Çmðåb’?#.‡jSD·áÕº¦±;”^$mã¯èIË?K¦öM‡Ìek®¨@Ôãó·ãóc6!ðNüKgì4›±¼ÎkÊ¯)òkú¼ñ?:Í>ÌuÄC¤gó²U*­
· ‡Í¹à¢){@¥uûá^	®ËS‹B{Ù£H}Rˆ{ c§”p¬Ú` RßÏ\ûšå¸PÑÿÁHð%Ð÷Š\Å¸Àj™äÖC&Ï@8í´qDxuçƒ³­¿ÔpÂù\.¨E‹V~!èäôädä‹:d~ß¼{øÓ·?ú–­ùÔä½U¿pOjÇ8ŠÇ{®º˜ŒW„Ä¢LcQÎTÁÿûÞ«Ú_{UµrM§mª)1Îƒ¤HNl“ÆÍÎ’bR‹¡¯ñ#5£½mGuâ—ÂàÂ¬88œ„'µÇñýV´66.‹£–öÄÁ‹æ‘—YÞ>’³ˆŸ5;YjÅqf?ÉÛNŒ­±6ßp$ßœ.ÏH{êi»(“°&ð0ò“0áÚÔ/’úšï]àQà/¬sÕr{µ/U¹MµÆ†bÜt9\&uÎ5üyq¹A0ð TP“s˜ÑìíOVeMÎŠ^R:×§à]STQ#SLÚl™Á1[’ŒÀvYÖÓñâÑõarñÐ{pïSãø= ‰ø’CƒW8·Ñ ½ƒ©Tˆä«·DôäÎZWM©y+#j@;ì§
¾ãºçÒý%¾WÏg(XâÆë@ÖOý@¨[TÜÌ‡â%îâI#Z5Ëvèý	¶«Ò”C^òMùhÒ« 0Îwûi£H·gú²ˆ|/GFt]S²|A#Ë(¶ŒFb.k¦Æøµ‹[}Dë¥A…g>Ã‹‡vÎ˜Zý¬ëjjúè¦ö ìåÈwä±æõåS]õó^à¶|[„[SÆ´Ÿ.YùW HO5Üd§)k@ê\7	Ï‚á1ëÐsGž·ïhéØ~*{rº,áT©ŠÖMµh»O»üJ:_ö.Çíôeœê?Ñ–U÷ÃªHëçÙ‘á&ˆïq5¼oztð?Ì(ðçkoÁ©´Å{H÷¦Ò†:fê)õÝ tP-%wG-–í§‹HŸãÍ&Ü8ïhG‡ß‡ˆÔ.ªÄï%š’º³óü"ia™€›ä9ª÷ãŸÅ’J]œŠžkóÓáâ5áO•‰¬¶,½–úÙßïÝAz­|–FêlµV§†RH×šÅ(õ®ñ9æR,ÍÔ6T.Ó¼GÕÐsÂ+L°Ü,ü:aìàHªÔÐ‡/Šù~ë3'uü˜#Ý]½XsØ½H@~RkßG¤ÎTBínay§ç¾®òV#ñ®®MÝ½G@kLuÝT.ra!;éáqõre¬Û¢w}ôdæ—Ço¼<Ö ¨·ÐÝ‚á­fŒñTî5Ìî:F·Åíæ&w°õ°Xon³Ûá™ZX–¸‰™µýëLl‹ÕÍÌë¦PulbVæñ•Êªº…½Ï6*SmdS[ ndR7Â©Eàj xhåx[šÝoiv)p—!²)ŽÛ*í^«´p¼VÖƒ‹âýl›ãg™âÂ{©Ý64ÃÏ1Âmip·eÜfÆ·AqÐ‡zÛVtÜµ`Ûww\Ümjj›WÛ:ïŽë¼ílfQz4øø^ì¹ÄæSí”­d¢†–ö¹ä-±Ú×yh+	Ým](¿b+“D“º)ËþëôRìDšÞéÊ…Ã¥ ~Qßïf»ºÿºÖéÛÝ×ÜÏüv@áù}¾èwßÕj
íx—Œ¦XGíjMµÓî+Ó—æwQõÛ à†*qm¯´]mMé(­í{ÛòçNùóHØs-îp°åÍò†u¼g.aJ$î:œ¹)çdµ¶®ä‹©¶t¹cº˜ê¡³%"ÁÕ‰ÚËëù}0¥ƒýÜ—¶f£ÔðFzÐ;#:¦·üvïvõ]f™ýBþÕ‡Úµñ# ¶®mŒ6MõÛ±nñ½O|eÂ
€³ ýŽ‡/Å.Ãa«Î’¼±z¦ÐÖ®¨–´Õv™/ž3j¡Ö,¾ç=ê¢uMØ•*’Å[ÉS©Ä»Uù~Ø©ÊÃXQGäH¼;Óþ?#ˆD®n€6ª]¾Xâÿ¯ƒÞ]Ã°§Wxe¿sÉÍÝ‘¨æt!Õ©µÛoÃéü®Á`1'GürüXÑÀHQŽ^öÁÃð÷‘¸±ÁƒúM>c .ÚºwÉüvÓÞØ‡Ÿß¾÷‚={—"6x¯eëFÿnôØl¤¯k     0707010005c826000081b40000000000000000000000015ab2175400000eaf000000660000000000000000000000000000003d00000000root/usr/share/doc/opensvc/template.service.fs.btrfs.conf.gz  ‹      í]ksÛ6ýž_™LÆÎ¬¤µ³Íç1õn“Ì¶MgÝf?t:6D‚Ö$Á du»ÿ}Ï½ )P~HŽ±»ÒL[$/Á{Î}—ÈÓ§·ùyòTÜâ‡Äev Ü¢RbìêÌÞXÜ-~ îvu÷äçÌ>ÝûåÉ+~Þ7þ‰Ÿ<¡aŸªÅÜÔéÁòîI­¤SÇ¦rÚ”§oñCŠ²‰©ä8WÝ=¬Ejõk£kµÌ{™[>RÕf¦-Æ£ËÉA|Iª2Ùän9üïMÉt9Uµv²LÂ}r%3ñVL•LÛA('Wµ?n+•èL'8g¢J\›Ði‰)gªŽäÛi®ÎXs¸·Mpà0M5©Jæ"èL8#*iù_7U"7àðÌäM¡‚‚!º(d™Ž"îéß”Àw¢”8OÖø“ãäTXåpÞ“W}l€äpˆ:=iLcÅâ2Hg“ÿk#°~¬\“14©¶§bR›¦â_3+»°N¸`¦¼M“§b¬"˜ºÇP©ÈjS04³	àÈŒ¹l¿(
ídK¬’tèËÃ!³jU×8GÛ³‘8ŒPbAê¬ª•åpþëòí³ÿ¼ÿç»w¿ÿðéè÷Oÿ/#Ç'^‰¸ð0¡ë,ë^hœ›ä´5R?é62]ÈI805Öa°„fMt.0-LSº‘øFg™ªUéZÉ‰,awäûÌ¦í‘¨Ò``¢±á&tí×ô¹]”N’·~…¯¯†ø7¯¯/‡ñåNòË€L
ñ®’£Åˆ¡|Ši#ñ!#TíPTFãom#pjUIÂ~Ç|0$¹•tSQã2ËÈ÷ºž‚Aÿrè<0Œ5>‡xÕÙÑJcõBykµK9Å–ÿçT2­V¤¥5a¾”ÕñõI™²Õ8'!ÆFZ§.YeôX«áA¥çMJéôLå‹å»ÿ¦"nvFF£çþFpMs²ZSxèGŸðäbïY›hDcãl Ö„ô¼jœ÷~tC$‚ŽŽˆqžÑÄ­}~ŽtÑrˆ0„q¦¢F$J†\}ÑO Ìš¦N”]Ö”š}§&r¼pÊ3¯#ÕÕÜ+Í1²UTîXŽMýøì¹;r['j){°­ø}yŸs)éØÜª\%0å àÄŽÃõ;ñ±RåÑ§¿‰¹ÎsTŽ1YMÀªz&Éià(¢œìÕþÌPùá)Á8Tw ¡ìÒþCŽÄ‘¡<†D;YOTœúè¢ÊUð*}5˜0 ãhª
—z[È¥upe)lEÙ³ç£åÃD‚v2ÒÇ Ð¾³J¦6“	ÌYÎÖ[©zØùžÙb,­öì]•{ .¡q59Nªæ¡Î<|¦½kSz¥v)8TýÕ¢ÅÏ4æ_ú3ynæ‚ˆGi3FÇÂ“c]Jp™ìÅüÝ ©w$þ†¿i’!öeÝ)ÜÑà8QËéüØì^Ð{Ã„kÀ	pÒ—ƒY¨bæÝ	õšzá¡x2é»è{žKº.¸„ÛZpÁ€c;…ø-ÄWC|A°Aö®&ªîÁÿU—*ïr”™Ì±4U¸* ××É?72ðõþÞ‹¯„×?|{Ù7€f8F›"mq‰P¥mêÕŽ¥!Ç{ VðQ¸zíâä‹ïA‘
RÉÉ{/ÏéEˆ"A‚hDeP	EˆŽíGÁABPÚ`u;,z”û^î¯cß¯qrK¾Mý‚ñƒ*Õß‰ñe$á“Ðu(Ø[ägIu&žÅ9ˆ]ºOË×È±Ež¿DItN`œÉZÊ©z$ÞÃ©3I	Î€Hô­.›3‘ðÈõ‡ûðz)OSæºÐŽîÍ9S,Í€õ¨VÇâ/¤q”M1ýðø…Njc4µ±
"qJÂð 7mÒ‘x¹÷ìkœd½ôu}A:´~,tÎ‹Þ‘477þÜ˜óLd¢¼—~uD=6¦8¦¹ÿÚä[ò_Iþ=ï¡Èr9»{÷©–”NøÎÒW©¶þgâÎÇÆæ!Æž¢ i|šœ–¯œs·…àîÞó2y{j½|égý\pÂ5ÞG¢Bxg—Œ;/Â²÷¡ô³.
•j<vÂå'òãw‘ ?Hï±q$üNÜ—µqêSPÙ"<ƒŸ`	L3$~d¶ûÒù=EÐ¢¢°Ub´Ì'bÖ‚éûk8ÎÆ½e÷uÙ}n]å}»IÔ?—Wîê2Âç$ž5nø1öl ã™k=uñ¬/ªF«Vœ¥´>
UÆÜ`´}ÈßãÏvØ¹¬*]BØ–"×¥ÈEðÐI±Ôj˜~¡m%lÇ¸EW¼_]…ÚlkÔÂ¨ÿDÝµeûÕÜîÎ!ûjPvŽHDO]ûáÅößk{ªrÖº„q~ªÍñ\éÉtK¯[qåuÞñˆ9Ï½¯Ý‘øD` H7Wª>œòî Q¨`#VtËÇ¾1Zœó^†,(åä#œËÞ»Î[èœ¸-®Ä'TÖñ´´£¤Ñ´êƒcïff‘Gê~b¬2ò’IZ.Ëä4¶e_ýxVGÙlBóŒ^+ò¸4''/(£DÝýù—çw‰›¥	ü^Íáçá2ÖØHÖcíjIÐá÷vÆæ~ï=a½yünU),=,Gia)¡“«Ë6Œôm5Ì;®`Æj*gâzÅ8-øûï93å²,Q8nhƒ/Ÿ“üq9T›"º¯.Ð5Ý¡ô"i£EOZþY2Õx´o:d.[sE¢Ÿ‡¸Ÿ³	wâçX:c§ÙŒåu^S~M‘\ÓàÿÑiöa®#"=›—­RiU¸)8lÎ­ MÙ*hü«³Ø÷Jp]žZÚËEê“¢@Üc ;¥„cÕ•ú~†àÚ×,Ç…ŠþF‚/¾Wä*îÀVË„ ·2y*Ài§#êÄ«;œmý¥†ÎçrA-Z´òA''ð ''#_Ô!óûæÝûÃŸ¾ýÑ·lÍ§&ï­ú…{BP;ÆQ<ÞsÕÅÔ`¼"$e‹r¦
þß÷6XÕþÚ›¨ª•kj<m“PM‰q$Ezpb›4hv–“Z­x©ím;ª¿fÅÁéüà$<á¨=Ž'è·¢µ±qùXµ´$^4¼Èòö‘œå@ü¬ÙÉR+þ‹ó0ûIÞvblµù†#ùøætÙ(x®@ÚSOÛE™„5‡é0˜„	×¦~‘Ô·xÐ|oèŠ a«–Û«}©Êmª56<ã¦Ëá2©s®áÏ‹Ë‚ï¡‚šœÃŒfo²*krVô’Ò¹>ïš¢Š™bÒfËŽ¹Ø’dÜ ¶Ë². ˜ŽŽ¨“‹‡Þƒ{ŸÇïMÄ—¼Â¹mˆí¬H¥B$_m¼µ ¢'wÖºjJÍ[QÚa?Uð×=—î/ñ½z>kDÁ7^r°ÆxêBÝ¢âf>/qOÑª!X¶CïO°]•¦ò’oÊod@“^…q¾ÃØOEr¸=Ó—Eä{É82jœ ëšåYF±e4sY35Ä7¨]Üê#ú[/*<óA^<´sÆÔêg]WSËÐGg0­°e/G¾#}4¯/ŸêªŸ÷·åÛ"äØš2Æ ýtÉÊ¿Ezªá&;MYRçºIxY‡ž;ò¼}G[HÇ&ðSÙ“Óe	§JU´6hªEÛ}ÚåWÒù²w9nï /ãTÿ‰¶¬ºVEZ?Ïžˆ7a@|«9à}Ó£ƒÿaFá€?_{N¥-ÞCº7•6¼Ð¹@0CPO©ï¡ƒºh)¹;j±l?]Døo6áÆyGã8:ü>D¤vQ%~/Ñ”Ôu˜çIËÜ$ÏQ½ÿ¼@(ÎTêŠàTô\;˜ŸF¯	ªLdµeé]°ÔÏø~ïÂÐkå³4ê¸Pg«µ:5œè”Z@ºÖ,F©wÏ1—bi¦¶± r™æ=ª†ž^`‚åfá×	cGR¥†>|QÌ÷[Ÿ9©ãÇéîêÅšÃîEÊð“Zû>"u¦jwË;=÷u•·‰wumêî=Zcªë¦r‘ÙI«§+cÝ½ë£'3¿<~ëà­à±=@½…îo5cŒ§r¯av×1º-n77¹Û€­‡Åzs{œÝÈÔÂ²ÄMÌ¬íoXgb[¬nf^7…ªÃ`³2¯üxPVÕ-ì}¶Q™j#›Úu#“ºN-WÅÀ@+ÇÛÒì~K³K»‘MqÜVi÷Z¥m€ãµê´ü[ï§`ÛÌ?Ë·ÞKí¶¡~ŽnKƒ»-ã63¾Šƒ>Ô[Øî´¢ÛÐà®Û¶¸»ãânSSÛ¼jØÖyw\çmlgë0‹2Ðã ÁÇ÷bÏÝ 6Ÿj§l%5´´Ï%o‰Õ¾ÎC[Ièn3èBù[™$
˜ÔMYö_§—b'ÒôNW..ñ‹ú~7ÛÕÅø×µNßî¾æ~æ·ÚÏïóE¿û®VShÇ»dì4ÕÀ:jWkªv_™¾4¿‹ªß 7T‰k{¥íjkJG	hmßÛ–?wÊŸGÂžkq‡ó€-oî”7¬ã8s	S"q×áÌ…L9'«í°hp%_Lµ¥ËÓÅT-	®LÔ^~\Ïïƒ)ìà¾´5¥†7ÒƒÞÑ1½à·{·«ï2Ëtè7ò¯>Ô®µumc´iªßŽu‹ï}â+V œíw<|)vÆ[u–äÕ35€¶&pEµ¤­¶Ë|ñœQûm´fñ=ïQí¨kÂ®T‘,ÞJžJ%Þ­Ê÷ÃNUöÀŠ:"GâÝ™öÿA$ruÔ°QíòÅÿôÞè†=½Â+ëü…˜KnîŽDåx4§¨N­Ý~Nçw‹99â—ãÇŠFŠrô²†¿ÄÔoòqÑÖ½Kæ·›öÆ>üüö½ìÙ»±Á{-[7ú‡p£ÿº¹çG±k   0707010005c827000081b40000000000000000000000015ab2175400000f12000000660000000000000000000000000000003700000000root/usr/share/doc/opensvc/template.service.fs.conf.gz    ‹      í][o7~Ï¯ r°’ÖN›·	êmS Ø¦Y M÷¡(lj†#q=3œ’É*úã÷\ÈGŽ-9±b«Dò\9çûx.äúéÓûüyòTÜãŠ+Ü½Š»Çw¿º{ò[ážÿþûy©–cóÓUs¹šÃñÑ=þ :\f9)U×Ð/¶UxÂª?ZmU~íDcÍ\;mj]Oéä²tt&W…lK¿êóÏ¦¦ºž)«½¬³ÐN©d!^‰™’yì„ð¸Êòy×¨L:ƒk¦ª†{3R
´à2¸â—™“Òd—¨)Qèï§O·t^UBWrNÌŒóÐYág*a@r­7¢2míÇâ{]ÊªÚGÉ™¬ÅD	§¼haj‘Ç+QµŽ‰Ö…FðÞoñX-+¸uY{yýÿ‹—¢0æÉÿÉúz8Œ;(	È¨AZDµ‚)°
”ï °±ø±@T†ñ(£áíp¬j$â¥rè" íƒÜFú™°ÆøÂjëVx*ýáÐyd#0ÕøÄ«n1&Ýø"õ‚ò6j÷Ü4«áÇÆÖ&hzâ¢QK·«Ò/›‡µ#ÍTv;×¹ôÊ…&rè`æ]Š¿ÄŸ`/þ98eá‹ºòÏùãKþø
>®èš‰·ô9/¤‡«´S|äñ¼uB¿T.7øÙá<UéZ_¡<-Y8žŠWÎº´öô¥cósnÿoÊ¿ýQ_áY“ÑqüÀ6j]†ÞtxØ·Wø@Ås:Zš)}vÿã%SìÀx<¾Æ¼Ô;.IábÐ©p0ïØSï¹òÊÂ3F]&¦73U8àu­SÄ_ör…ÆMvµlÎþóà#*àïüL»^ŒãÄ¤ÕeŽq‰$•ác!Ò:“¥˜›²­ºžC¬¥×sU.W7‡D¹!’á7Þ•Â&3WÖjŒpúTxrqrü…0ÅšÍˆhZÏôÀÝÌx:9Fž%âº“Ðtf•D‡=áNŽ ’‚~æÂB0•ˆCø s¦µ™rëÂÚZ“ 7j*'K°D¾ŽT·›ÏÚœ7V©ªñçrbìþ¹¤îÌ®Œ)ö¬G×Nfj IÒƒ‰1ÐT½N`£÷†âm£êw¿~'º,EP9ôÉiVÙ¹D¿g!P“	¸ñÊh^tŒ“9’Pv'ñýCÇâÁPE{i§*ÞuÕ”ª‚‘šsà€Ð¶iàV¥t¼qcE¹«gãÕÃ$‚êc€Ž`t:X'Ó ¦2ƒaH,§ÑÛ(;êE­Ìl1‘N3{×%‰€hÜLÏ³¦u{ÇÞ[èX›+ WîV‚Ùªœ7S-~Ã>ÿÞ#èYYš…@âaæ=Ã‚¹†˜èQ“IVŒ[àP½cñü/ ÅHmYw‰îhàpÂÊ2ÒSq<<>ÇÇ£çˆkÀ	àÄ7ƒY©ê æîÀõbtH¹ü8ÁòMra¾3¸ˆÛFpçnâßñœ$ jªlþ+[«²‹Qæ²¥q’FåCHWu6ƒï­,pøäøùW‚õ¶½î¹€f4D,‡°Å·ä$†BÕ®µ!ª	Ë?´¾‚Î‚©×>¾¨ôT <[y
_Ð#@‡Ðdàpæ*¨=DGŽø#‘à„è†ý`gÑ£°ïÅÉ&öýÑ/äÛÖ¾ `)ü@
§2Ã3!ˆO&=}GšD4µ¦m„.ÄiâVfÈOK÷È‰ƒd0AÐ¡kÓðHZYaZ6?€!RWœ!’è']·W"£; Ö€Õ“ò´u©+í±mŠ™RiXùÂz_øFìGÝV <~¥3kœ‚š»T‰8%aàÞ´ÉÇâÅñßÂ5AÖÎ#ð êÐq_ðšç½+i~aøÚ”óDd¤<K¿Ý£žSC‡½5åü·’KË{&ŠRNÅÑ1zÁÌ%¥œÀ9<”kÇß‘;o[˜{	y Jëäp½|¥˜;&‚GÇÏ†É»KÇò¥÷õSÂ	¦¹bœˆ
îL2´¼$Wm(~×U¥r]†ŽPúI|û&Äd‹gÂï8‚cçà¶èw@}
T¶ÏÀk)q’{æÚ	§Î_Ó¼
k	¼x+ÀV‰“ñ*žHYL?ÙÀqÜvß•Ý<ý”Pû59}·×¿Wé:Áæ$žµ~ô¶õÆ ôÈlõF-\@ÖèÔšñÃð¡‘)•rƒÐf—L?Øá²itÂ¹+E>d ßtR¬´&Ú²kn;Å-¹ã¥øê6Ôæ‡Aý(õ?²]l’´j9D_-¤$Â§¶Ü½tüGbÍS‹€YÎF“0)/µ9_(=èu/á_Tªðã[xÄ’æÞkw,~E0OH¿Pª|(ä=€Æ!ƒMXÑ=,ø¦hQÌ{²@)/÷p.ûøF8?b{´(ŸbZGÓÒƒFÕ†½›™…8RW`'&ª@Q@0‰Ë%Ù[Ù_Ïê1šÍpþ€Ð‹"_ŠAórºNRÔ£ß~¶KÜNà÷rž‡+Hccqf'Ú[‰ÐÁïqÆ½k–l‰Ð…Þ<~·ª–V½ia)¡“«ëÆ0< WþXÞi3Q39× ®—Œã² ·?rn4¤Ë²†Àã|A¶|ŠLâ+ÒtÈš*i†VðžÖ0¼È¢£Cø¤õ?%¯ºbo_vÈÜ´æ
ˆÚ?q?6?e8Þ)Ï±tƒg3V÷±¦xM‘¾n(máÁ¿wš}œëˆgž-ê¨T\Ž ƒM±À…Sö •Î4|ê"µÃ½\×—máf¦-s²9âp°cH8QÑ¨œKr‚iß°2ú¿	}Vä:î€ŒZ"šõÉPN7k=R']ÝùÑ»h/5ár!—Xeˆ+¿ èâ,èÅÅ˜“:ˆü¾ýÃÙûŸ~áªÃÅÌ”½U¿Ð&Š}§ý½–]ÌôW„À¢ÎSQÞ4ÁþsmƒSñ×ÞD•U¾µð´m†9%ôó4«òÓ×æ©Csó¬šZ1râøŠõ”¯b¯.x)L˜§—‹Ó‹ð„ãxž _M}ãê±Èki~$r^8¼êÈªùDÎª#<kv±Ò
¸3OòÆ‰±£kæäþÍéÒ  ¹é.™¶Ë:kÓ ÿÇ€ÃDL(7åER.ñ ²1®Š þÂè\¹½Ü³Ü¶Ù0†‡bÒv1\!uI9üuq¥gÀ• ˜P£q˜ãìí{§Š¶$E¯(]êKà][5I!SJÚbÁ#É€Œ[ÀvSÔ(æ“åÞõqrñŒ-8ÛÔÔq"¾&×À
§²!ì4X‘Kž|½vÜ™ÜE4ÕšGIÚY?Tà—z&oáZ=Ž!aIßä ÑÔõË†Šù yIëƒhÒWe`uAãª6õˆ–|s4Ÿ— IVAe<Éó´Q"‡*Œ9-BÛ‹ƒ£ÀÂ	¼¯­²r‰=+Ð·ŒÇb!-QãT|¹‹_Dnz5 Â3Ÿ†î¥]»6˜¢~6U5E†îÝ€‰ÂÕxyÇyd£i}ùR7ý¸pë’[[§ÄŸ.Xùo HO5Td§1j€ÐÙ¶Í‚ÁcÚPs‡–·oh+éi¼¯{rº(áR©×M³ŒÕ§]|%=§½«~³¾‰Sý':°êó°*Ñúuö$dø¤mÜÎ¶M{ÿãôÂº÷ŒJLÞC¸7“˜©Yx°%83pê9ÖÝ€ëÀ*ZîÞE,ãOç‘†ãÍ§T8ï±ïÎ~).ªt=¡T«³ÎÊòCÒÂ2É“Wïû?Š3(«"(½VÆÓáæîOÕ™l,ÝKyö€ë½;C­GiXqoðôsu,8Ñ9–€t¥Y„RïŽ1Wbq¦¶u@å:/{T5'´A+Í’×	S‡R¥}pRLímŽœÔù>{º]½XsÖ½H€~f5×©+•a¹[XÞé™¯Û¬ÕX¼¶ÖØî=\c²¶m|bÂBtÒÃãö)äÆ8@ïîèÉ‚—Çï¼5<6 P »‡·1¦S¹wvwtÜ>}ÈÝl=,6·ý¬vxDC-,K|Ê0‹õ›†Ø«O^Ÿ
U‡Á6ÃÊì_úñ¨FU·°÷ÑƒÊ4[©PŸ4¤>	§ˆÀí@ÑF •óCjöyS³»	‘mq<diŸ5KÛÇ;åi=ø(~ž„m»ÁøQCñ ágÉÝ¶†3©ÁnÓ¸íßÉAêl;Íè¶pw‚íÜí8¹Ûv¨mŸ5ò¼çy[³M˜%èyÐàþ½Ø³Ä3í•kd¦F·j¥-±âë<¸•„æ Ü\TñŠ­Ì2˜Ø¶®û¯ÓK1H4=èÒ…³• zQÿªÁ7,×ã¿±:uôÕ3¿âFx¼ÏþÎU­¦ÒžvÉ´ÍÐy,Wk›AÜW¦/7æm  A•ùX+íÖKS:J ‰6Ö½ø³Sþì	{îÄŠ¼Ù)oHÇ[qæ¦$âîÂ™2åš¬Xa›ÐàV¾˜æ@—ÓÅ4-		nwLX^~nŸƒ)ìãº´¥†7ÒƒÞ	Ñ	¾À±À­¿Ë,óo Ä¯>XýG@mSÙnšÊÛ±ðýœøÊŒ Æ÷;½Gc‚­ºÊÊÖé¹‚¶¦`Š¬ÄÝâërùŒPû7Zspœö¨KvÔ5aWªDý5L•h·*®‡©2ì•TDŽÅë+ÍO#¹¾jØ¨võb	ÿ¹ŽÞ]£°§Wxeˆ…¤âîDT	æu%Õ±´›·áô¼k0°˜‚#z9~¢°c¨(/ûÀÃÐñDÜÄÀƒò&Ÿ)ÚºwÅü¸iojÃ¯oßû={W"¶x¯å`FÿÆf´1M[ò^ˆ;G÷#Â©xËÎÿ.Ã:rB]Û%ÆU¶-½šLý¥B{z3“Êµñõ=À1TFóf÷aõU@s…žV²ƒ´K~Ÿ–f"oÜ²º,Ü#þk$†–›•êª¿}Ö¼n[‹*2ç(~ïýÕ’ Î8IöKEù˜ul    0707010005c84d000081b40000000000000000000000015ab2175400000ef9000000660000000000000000000000000000003700000000root/usr/share/doc/opensvc/template.service.ip.conf.gz    ‹      í][oÛÆ~ï¯X (”àÈ¬m$}p£>½ Ái.¨Óö!ä¹”"wY.)YÁùñgfv—\ÊÑÅµ•Z=TCæe¸œï›Ûî~òä!?_=aøAq²xPqøq«»¯>ÈâÉñÇ¯pœ3±\è29k/W-;Žðƒú0±.ø8Í•Þ—5^‡•âÏZ–¢ÂÏ<3´§(õ\©•T“³ÎžD¤¼ÎªvÔo´¢RME)+®bw¡Lð”]°©à‰…`pÃ¢´ûM!b™ÊŽ™çÆxXÌU"^	sÖÊgÿeq©ë|å9ÿ¤|It<%|™Ätˆ’¤X£‰áì÷S¸^!”™ÇÈ±¤”s8Zñ\DpÜw¨möÒŽ ²HÄüoEÄïø» YQ§T•(SêF52
]Le<eoAÑW¿ÿÀ2ËXU.Y¥™©x<cœgD9—±¬Sû+6,‘i*J¡*––:gˆ·Ò‰@	x®†å.í®s#pG lð=ž€C P¨»‘Ö$€j½k<ÿÿÛüVÐþñÍ•Ã¹d¯Þ1ž$¥0†é”p“‚_u]Æb”B;ºÆFØû©4þÏµ©XmDZgD7:vÉx0äb*ÔAËÊà(~üõˆKÄ/áØz¬ìâð=“3‘¡;wXM¹
dUS·àt &u\‚|ØÚMÄþÕ”ö;f]Òp…Ít<cç<Ët®ð‚ÍyVƒîD–/ßìÆÅ‘7®Ä"T€x8Ýç,Ýh
‰2£º@'|pæÐìÙW8ÂÑB¬i®k#k—RcºUcè€µ/
‘+åd"J`™šUyD¦WŠÆQOÀ¦ÈÀšê2çnæ†CtCD/¢s^ð3çŠO`ðK\Y­2´Y„ÔÊ97ç?0¢®¯lÑ–ØûÜÀ”62ušÊ›ƒ#Ë¾|çe’•Z«åˆ¯Õ÷~ˆ¶C˜Ò‘Ï@ÞE¨šÖfîGç+0¬2!Dœh@ÎÙìÀÂ–YoN(„%šPQºâèò‡6±ÄEÊÀaxxo"v™±,aã%ä’Û6qî$"ÇA»[Lü§Ô5ì“)5‚Ý#·ì[TÄ<Ð-ïÎÐOŽ#úoòŸ¬{Ø=ìŸ(‘Áº’¡ž3©f `¥#ö*E­ýQÿ]du>†ãˆ%xÀ$Ócˆ÷Ò¦)t	+•¸©.Úð’¶Ÿ¬JÜÚT>>d¬^Å™4U×1c¶	æV@FvdFb4\<3·óB—ÕÅ7çp•Žuvñáì|Š9&mÿ@ˆòJ9Æ˜nÐN+Ÿ\¢«æ×K”³«_·àHt6‘üÉ	A-ëín+•“ˆFã ÙE†+iMü°ØC@›ë=xÎMïÁ=À<¡¾ Ã'7Y6f©­–ZWA½ûtSýéÔ‹Ò‰kÜX¨Ï"öš¡¡.–®zj$w|œ.c"¥K¯ØÓ¤ÎóepÚ­Ê„ÜGGŽô™M.lî€K/³ ²ÓqÎ'¥oþ|l=dœ¾xÙ§Ñ1~z:Ý%ÝÓ¾¥ÓØ·Ï}|!u¼Î~±>È“Q\ÔæŸEÎ;ø­DB%¦lSÀQ1AL?à˜?vÝ¤‹†0âS|dD`Tf3ˆ&­g¨^¨Ñá'ÒfµÔ¤C¸qn°d%WÁÎØñðdxŠŽNV‡ÀŠÖƒ™‹¼s`‚zÑàT‰	gE^ÛÉ;Ü\Äm+¸À€‘™‚øâÍ&Aç=Á €ÿ?¢T"óƒu%#¸i,†.âÔ¦†Ja‰›OŽOŸ3«H2ã¡»/ê£±®5lÔ®ÊÔ®ñK4Š¼ÎšÊb²
c]“"ŠI®ŸèÃ`ÂcŸ±B”8±%3áU‚±³!‡ÿ`‘³°fýšT)jŠ£°ïÅÉ6öýYCÕ“oWÿ‚€9¤Àøª-1JšŽõE¨Ï8¼š@ýX`ºòõšÙ" WfS!ÆÇFgXmº‰JM5§+h:Ã:ÈO„EìgpDâ†çE&†H¢_¤ªoXLg˜!;:¯Ç•ÁÙ,™CŸØ$d¿M±ç·ÆbO¤’ó»o?—¸œ$À@ª '8èMê$b/Ž¿þŽq²à7‚P‡ÆŽ9íN/´=6ä<)o¥oŽ¨#­óV…¥Îzòo$ÿŽž÷’¥Ÿ°§”Õž<³…›P¨ƒ›iìwäÎÛš*Dcg2Ë:9˜sÇW*þ­œ¤?ƒš’aWâü]^ÐtÒÔ¹õÀ(ÞÉ%Ã•—Ê½Åï2Ï¡H€ÛÎÜ@š)£·o_‚ì ­Ç†=îw*Üàà4w@}T¶t÷ ¡P	<~d¦›¥ÛøŽ¦¤¬– z@´l;‰Ú|"d-N7má8wÏî»²ÛÈO¢CíŸ(è›]¢þ­¼
Ý ÷ñ²ÆÕ`GoÓ£ŽÀø+¿ŽëÊ–Ç]ç‡éCkTjrƒÐ¶!ÿ˜>[Øa4óÂzŠÜ•"Ÿs€W g­V]ÀD_v+l‡¸g¼dÏ7¡6ïúQõ¿²}[6Ij¯ì²¯ÊÎ± á]—vx¡ý{bÍC€UÎV—0ÎfRBN¦=½Ä!ü²à{õn1ã”K[íFìwó€¬B(À‡RÞ (rlgÓÝ,íwø†hQÎ»Y ve¢Ç6O½#ZT‰O°¬{®œúÀ±7+SGÊüÄX¤è!RH&qábÌãpùÐU9Ük…Ù,u¯z^äK¶´ŠO/(C‰úôÃÇgûÄ.±Rsøµ&ÔXÄ.Ë±¬JŽÐÁï¾é‰–ë-n• ó`@5ÅXîGÒ"v¥q™ÒË•*†´«Ë´¶†1ås	â:Å8(m¯?`|®%”Ë\-±o
ý|ù™dË!¿²éœ@žS›¦±b´	ïT}ÃmO&ŽöeƒÌ¶a"ÏC<ŒÏÙdÛ¾hµÊ;Îf´çYM‘jí×ÍÎøN³³ëëÒ³…òJ“n@r›r+ìÚÃÆHSËX&Â¯¶PøO5•jf Ðffªë,!OQP î0 SÂ±ðÁÀ5vy×¾­«ËVôÿ0üè[E®âŽKµE„@·î2yÊÁi¦u…Ô	Ww^UÆûKZ³^ð¥ÁØ ‚®¯Áƒ^_G¶¨Ã~ÖŸ~¾üí—÷¶Ÿ`1ÕYgÕÏ]—«Ý£p¼·ª‹©†ñ2—X¨°£ÌTºpþßP`„ÿµ3QUŠª.ánëkJçYœ'g×¦NÂ€fæq>)ÙQÐÞæGum—Â6åžÍg×î#¿î \uhcc{[µ¤½%
^8Ü¤½| §ˆ5»nµb7Ü†ÙNòú‰±-Ö¦T?¼9]2
š+À¥~¢íRÅnMàq:äw&bBµ©]$`WÜ ç{)
 Á:W-·Sûb•[[lxÈÆu“Ã¥\fTÃß—é	víâ©XP£s˜ãìío¶½ÝRû>XRçKe&ìlìJ‡°Ïàˆ‹žd@Æ`[—uŠÉxypD}œ\¼´ÜúÔ0~q"^Qh°
‡q´u°,á"y·mN"Zr§ÞUSwš“Ñú&pôT² dzgw	$,¶¯€Ö<9Hc4õB«e!c /G4š4ÂUC`Ù û¢È®”VG´ä› ûœ&­
rqÓFœ\×ÊµE¡ïEãH±qÏ«@–-qd)Æ–(b^5ÎØP»T«·h/Ý”»ç37¼ph·ŒÉëg‹cozpã…=*{¹²=Ìä£i}y&‹nÞ¸57IŽ­V!þÓ$+8ŠtTIK‘IÌ u.ñž9=DH+çÊzÞ®£ÍyE&ð›êÈi²„™®êbéŸjò+^Ù²··uÐë8Õ½£žU_†UÖo³' Ã}^c3¬o:8øgvøÓ¹àT|ñîÒ½)o»‹Ç8ož`ßÍŸP,&wWKÿi"ÒÐæxó	¤2«pW—o\Dò‹*íC%XJbwÖe–}Nš[&@VÚ¨ÞV Ç'P@*vEP*z«ÌN#¸“·„?¡b^ô,ÝKíìm.o?±ó€6KsO¬ÔêØp"liZ³¥Î9þY´ö©zöªå$ëPÕõœÐ
,ÓK»N:8”Ê%èÃÅt½í™“r¤ÛÛc… Í<§Þ{ÈðãRÚ>"q#blwsË;÷µÉ[Eì§²Ô%`gSjz´ ¬‹*pa.;éà±y
¹ÀçYzôîŒOíòøƒƒ·‚Çô êº0¼ÕŒ1œÊ½ƒÙÝÅèzÜîor[‹íæv˜ÝÈÔÜ²Ä}ÌÌ÷7l3±«û™×}¡j0ØÅ¬ôá•Êªš…½¿lTºØÉ¦z îeR÷ÂÉ#°¨16‚VF}iöeK³µÀ­CdWû*í‹Vi;àx§:­â—)Øv3Æ¿dŠ=„_¤vÛÑÿŠö¥Á~Ë¸ÝŒo‡â uÛ^+ºîN°õÅÝž‹»]Mm÷ª¡¯óö\çílgÛ02Ð‘Óàá=Ø³Ä6¾:_%!m à˜»bËãX &e­T÷qzÎ¦M¹pÙ
¢õo
|Âru1þ¼”ÉÅÓsêg¾FQôÌ¾p·]­:—½%cPCSa»Z]ü{eºÒè9÷ ¸ ˆ+ß+mV[SJ ‰¶ö½õüÙ+„=wâå=oöÊÒñNœYÃ”@Ü]8óY¦Ü’å;llä‹.zºì™.ºxìl	H°K`õGÊÿ+±Î2Ð°Æ¿¥b¯P
ûÁ^½»|¼‡º¶Ý²í<‰{kmÇá|Ò°‘¼•	cÁëS%_‘ŽýÒÛžÃ7†c×fû7È>|“ÝÒÈ$N'þÍðöý:Í»ï}Màºùê‹^»¯w]y±k êÖ+^l¾˜øõ@-¶cj     0707010005c828000081b40000000000000000000000015ab2175400000e93000000660000000000000000000000000000004100000000root/usr/share/doc/opensvc/template.service.fs.directory.conf.gz  ‹      í]koÜ6ýž_A (œ`gfílƒnÔÛ¦@°M³@Úî‡¢°95Ãµ$ª$5ã)öÇï}P5ö<Û‰§Ð IlitEÝsî‹¼bž>½ÏÏ“§â?(.s#áW•©¶*ñÆ®î$î? î~u÷ä·Ì==þýÉ×ô¼¯º'~ò‡~©VKcÓÓn•ôs81¾ÇªÈ%¦’Ó\µwúÙÖ
OXõGcJ¯¨¬Yh§M©ËüAæŽÎ¤*“uî»AÿdJ:¡Ë¹²ÚË2	÷É•ÌÄk1W2m¡<¯²|ÞU*Ñ™Nà;3UÂµ	iîàøÆÏs%²:ÏQ'ÂdÂÏcÊx#«¤W¸èkúÎ+‘³I³µSö³j¶Uà#Q-*t*½psSç©˜‚„%|÷š®'â††!Êº@ip¯ÈôÜª˜šŽfÆ„	%¬1~ 3kêj@$B„4òÑDx{$,ýœq½²Å€H„*D;‡[s? ¹\¨‰8Î[)<Pe•S¥Ç_à‚“ÄË<È~~LN¾úê«M ¤j1D„’in’KTŠN <è¯§×^BrNÌ»ˆè»C
S—~"¾×Y¦, ×HNd‰Öç”`5¦èÃ7"Q¥#7Ák¿Åc¥,àÒUéå‚‡·Ç¦?Y_ŸãÇfw¨AZDµfFÊ4ñ6CTFÍPTFÃßÚEà€-JÄK¥0D@Ú¹”*``ÊØé^[á)ôÏ‡Î#³ÀXãK¯Z;bLZû"õ‚òvj÷ÜTŸWÃÿ¬MÐJ(‚–¶«Ò•²:wúÏÁ™4Ê|‹y“v½ áÄ´Ö»Á©KR>–ÈÍL'¡&‡|
ƒJÏ›”Òë…ÊWÝÏþ‹…ìâ
ÿžL&ÏùFàš(æ˜…²VcxèGŸðäâäø‹&©‹Æ†ØQecŠªöìýð†mx:9AzÄ™wsnÍå$<È1„!g*,D¢dL(þ€9SÛD¹uau©IÐ;5“Ó•WÌ¼–TÛ¹WšsÈ}TQùs95öðì¹=s/˜A¤:DÜi7Zñ¿î>‰)$Ñ¦ÆÀ­Êucœ8òpýÑH¼¯Tùá×ïÄRçO²ÊaLN#°Ê.$:8QNFà6ßj02xJ`œL‘„²=‰'è"8æ1(ÚK;Sqê£‹*W„Wº©À8êª‚KÙré<¸²lE¹«ç“îa"AGêãŽ`h?Z'ÓQ“ÉÌXNÖéó¸ò™Ùb*fö®K3 ›J ÙyRÕîàØ»ÕÞ‚µ©z¥®Ì^å¼ša©ùŽù÷AÏòÜ,Óf18LÔÄT—ˆ¸Lòb|7 Õ;ßÁßò³Ø—µ_‘ÀÜNXYB:*ŽG'£xàxü‚
'Æ	àÄ›Á,T1€ùp`‚z±¦Bhaù.:Ž0ß\Äm'¸À€s7ñÄÛ!¾!Ø@ö®f8%Áÿ/eK•·9ÊBæ±4V¸*A®¯“9ü\Ëx ‡OŽ_|)XÿàÛË^¸hÆSÈbSH[|MAb$Téj²šÀ±Ô AÐñÃ= VÐYpõÚÇÉÝ#HE'Ï^žÒŒ5O¬$8ï’«F%!Zr4‰¥ÍŸ…q„Ù˜˜RÀ¾—'»Ø÷Gm¼È·¯	sjˆ?AÅú;1\F">‰ôô3Ò¤qA<y©3ñEœƒ¸Î­ ¹àié9uÃƒæ/HÂ¤tM`œI+å•ˆÀ©+‰	ÎIô£.ë+‘ÐëOÀëIHyê2×…öxoÊ™biXõÂúXøBGYSž×-tbS` ©‹U‰Sô¦M:/¿ø¾d½ä: ¿ó¢÷Hš_þnÌy"2Rž¥o¨çÆç0`oM>+ù÷ô¼g"ËåL<;Æ(xòœ¸€sx(ÕŽFî¼¯=bbì%Ô*^Éàû2ð•rî¦|vü|™¼»t,_zOY?œàšöÀ‘¨ÞÉ%ÃWÊŠ?ë¢P©†ÇÎÃ@¨ü¤A¾	âA²Ç†3áw´àfppYw@}
T¶
ÏÀ¬1q†„Gæê)—Î_ãS-Aô€hØ*q2éò‰˜µ8ç¾ƒãdÜ»oËnž~Š¨ý†‚¾Û'ê_Ë+Ÿé2Âæ$žÕ~ü>÷l Æd¶zÖE³¾P5:µæü0}¨¤CJÅÜ ´9äÓg;ÜRV•.AØ@‘ÛRä&ø “¢Ój˜èË®…í·èŠWâËm¨-£~Fý7„ì¡-›$uwÙWeç×Ùñ©-/¶ÿ†X‹Ø#`•³Ó%LóKmÎ—JÏæ½îÅ!ü“Öyß¾‡GÌiî]°v'âWó€ôK¥JÀ‡RÞ h*ØˆíÃÒù€oŒå¼›Jyy€sÙÇáüˆyì}Ð¢J|†eMK{nÐ
êÇÞÎÌB©ðS•¡‡È ™Äå’©L.c[æê¯€gõ˜Í&8@è5"_‰ y9;¼ %ê³ß~þ¸9œÀïÕ<—‘Æ&âÌNµ·¡ƒß›wœûA<Ø2a½yüvU),=t£ia)¡•«ËlÛ&,˜w\ÁLÕ\.4ˆëã¸,À÷?ra4”Ë²„Àã|A¾|†LâoÄå5EtZ]Àkjw„éEÒD1:„OZþ]Õh´¯Zd6­¹B¢ÏCÜÏÙwÆs,­±ãlFwkŠ×éÇ}lü§ÙÇ¹ŽxéÙ²l”Š«ÂHÁaSnpá”=@¥ÿê,öÃ½\——
í¨S*uÄ= ±cJ8UM0P)÷3×¾c9.Tô1|ôY‘ë¸.`µDtë!“' œn^{¤N¼ºóÖ»Æ_jpÂùR®°EW~AÐÅxÐ‹‹	uù}ÿæ‡³_~ü™[¶–s“÷VýÂ=AP3ÆI<ÞkÕÅÜ”ØßÊ‰E™Æ¢¼©‚ÿçÞ§š_{UVùÚÂÓÖ	Ö”0ÎÓ¤HO/\ÆÍ-’bfÅØ‰oàGlF{ÝŒê‚—ÂÀ…9qz¹<½O8iÎÃô[ÑšØØ=E-ÍDÁç‘»t·ätáY³‹N+|à:Ì<ÉÛLŒí°6n8’‡7§KFAsÒ]2mWeÖ§Cþ·€‰˜PmÊ‹¤Üâó½¡+xøÖ¹n¹½Ú«ÜºÚaÃ#1­Û.“:§þº¸Ü@0àN,¨Ñ9,pöö§²:'Ew”Îõ%ð®.ª¨‘)&mÖepÄÅ†d@Æ=`Û”uŠétupD}œ\<cÎ>5Žß#œˆ/)4°Â©mÍV¤RA$_o¼u@D&wÖ¸jLÍQÚY?UàŽëžKçK¸W³F(XâÆë@ÒMý€P¿ª¨™Š—¸?ˆ&pÕXv¬ÎÈ®JSŽiÉ7E÷y	šdÆs‡1OEr¨=“Ë"ô½h6Nàuu	å+Y†±e2Ki‰§â{¨]üú#ò­;ƒ
Ï|†íš15úÙÕÕÔ0ôà¦ö¨ìåwä‘¦õåK]õó^À­}HrlucÐ|Údå?"=ÕP“Æ¬Rg['4iCÏzÞ¾£-¤'ø¥ìÉi³„K¥*\4Õªé>mó+é¹ìíÆÍzãuåÀªOÏªHë×Ù‘á.ˆï±ì›þÇ…þtí=8•¦xéÞ\b¥fáÁVÌ ¨§Øw¡»h1¹ûÐ`Ù|Úˆ4âo1£ÆyãøpöSˆHÍ¢J;*%±;ë,Ïo’–	¨Iž¢z?þ±@PœA©ØA©èµv0žFïªLäá½jz,åÙî÷n!½Vœ¥aÇ…ºZ¯Õ±áD§ØÒ¶fJ½k8ÇìÄâLmí€Êeš÷¨zNh‚–›¯Æ¥Júà¢˜î·;sRç‡éêÅš³öEÌð«¹H]©ÛÝÂòNÏ}móVñÆZcÛ÷pÉÚºò‘ÙIíSÈ•q~@ïöèÉŒ—Çï¼5<v PÐÝƒá­gŒñTî-Ìî6F7àvw“»ØzXì6·ÃìvxD¦–%îbfMÃ.°º›yÝªƒ}ÌÊ^ùñ¨¬ª]Øûh£2Õ^65 u'“ºNÛ¢`@+çCiöiK³ÀmBd_‡*í“Vi{àx«:­ÿ€â§)Øö3Æ2ÅÂOR»íi†c„Cið°eÜ~Æ·GqÐ‡z€íA+º=îV°ÅÝwûšÚþUÃPç=p··íÂ,Ê@ÏƒïÅž‡Al9×^¹J&jìpŸKÚ«y·’ÐÜ€;3*^±•I¢ [—eÿuz)Ž"MµåÂY'ˆ^Ô¿Â½mÝúbü7V§¯Ÿ}CýÌ¯G¸ïó…¿sW«)´§]2Žêjä<¶«ÕÕQ³¯L_ï¢ÊÛ ÀUâ›^i·ÞšÒRH´³ïmàÏƒòç@Øs+îP0ðæAyC:Þ‹3˜‰»gndÊ5YM‡mDƒ­|1Õ@—¦‹©;["lLØ^~n—Ÿ‚)-ìá¾´¥†7ÒƒÞ	Ñ)¾ÀÛ½»õw™e:æ„øÕë›øPÛÕ6†›¦òv¬¾Ÿ_™ÀYà~Çã—âÁa«®’¼vz¡F ­¸"+q«í2_='Ôþ­98N{ÔE;êš°+U$‹¶’ÇR‰v«â~Ø¹ÊÃXQGäD¼¹ÒüŸD"×7@Õv/–ðÿuÐ{£köô
¯¬Ó±”ÔÜ‰ÊáÑ¼.d :¶vó6œžwSrD/ÇOåñex:‰›xPÞä3â¦­{;æ7›öÆ>üúö½7ìÙÛ‰Øã½–Áþ%ÜèÿáæÇµl   0707010005c829000081b40000000000000000000000015ab2175400000eae000000660000000000000000000000000000003b00000000root/usr/share/doc/opensvc/template.service.fs.ext.conf.gz    ‹      í]ksÛ6ýž_™LÆÎ¬¤µ³Íç1õn“Ì¶MgÝf?t:6D‚Ö$Á du»ÿ}Ï½ )P~HŽ±»ÒL[$/Á{Î}—ÈÓ§·ùyòTÜâ‡Äev Ü¢RB¹[w‹ˆ»]Ý=ù9³O÷~yòŠŸ÷=ñ“'4èSµ˜›:=XÞ;©•têØTN›Òâ”á-~HM61•çª»çu£è@­~mt­–ƒy/sËGªÚÌ´Åxt99ˆ/IU&›Ü-‡ÿ½)ù€.§ªÖN–I¸O®d&ÞŠ©’i;%ðäªöÇm¥éçLT‰k:-1åLÕ‘|;ÍÕk÷¶	¦©&UÉ\	gD%-ÿë¦Jäf¢ž™¼)TP0D…,ÓQÄœ#ý›øN”çÉrœœ
«Î{òªp1B§'i¬X\élòcÖ€ƒ•k2†&ÕöTLjÓTük¦seÖ©Ìt¢€·iòTŒUS÷*Ym
†f6™1—a`íE¡=ð€l‰U²‚}Ù¢³b8dVíà¡êçha6‡J,HUµ²|Î]¾}öŸ÷ÿ|÷î÷>ýþéïÿeäøÄ+±&teÝ«s“œ¶Bê'ÝF¦£9	¦Æ:–ÐŒ ‰Î¦…iJ7ßè,Sµ*]+9‘%ìŽ|Ÿ€yÁ´=#UL46Ü„®ýš¾c#·‹ÒIòÖ¯ðõÕÿæõõå0¾ÜI~I!ÞU²c´1”O1m$>d„Ê =Â ŠÊhü­mN­*IxÁOâ˜†$·’n*jc\fÙù^WÂS0è_f±Æç¯:;Z	c¬^(o­v)§ØòÿœŠC¦Õª‘´´&Ì—²:þâ±þ!)óC¶§á$Ä¸ÑH«àÔ%«Œk5Ü#¨ô¼I)ž©|±¼`÷ßTÂÍÎèïÑhôÜß®‰cŽA¶Qk
ýèž\ìï=khlœÀšžWóÞnˆDÐñÁÑ#Î3Úƒ¸µÏÏ‘.úA†0ÎTÔˆDÉk/ú	€YÓÔ‰²«ÂšR³ ïÔDŽNyæu¤ºš{¥9F¶£ŠÊË±©Ÿ=wGn+ãD-¥S b–£¿/ïs.%ƒ[•«¦œØq¸~g >Vª<úô71×y.‚Ê1&«	XUÏ$9E”“½ºÁŸ*?<%‡ê$”ÝA:ÀÁ‘82”Çh'ë‰ŠS]T¹*^¥¯S`MUáRo¹´®,…­({ö|´|˜HÐNFúØ!ƒcÚwVÉ´Óæc22ËÙz+U{!ß3[Œ¥Õž½«’@bÀ%4®&ÇIÕ<Ô™‡Ït ×`mª@¯Ô.‡ª¿š@´ø™ÆüK"ÏÍ\ñ(mÆˆáXxòa¬KB	.“½˜¿€#õŽÄßð7M2Ä¾¬;E‚;Ü'jY"?{ƒýÁúboø‚p8Núâr0UlÁ¼;0¡^S/|!O&}}ÏsI×—p[.pl§¿…øjˆ/6ÈÞÕDÕ=øÿ¡êRå]Ž2“9"–¦
W¥äú:™âçFæà¾Þß{ñ•ðú‡o/{áÐÇÈbS¤-®á 1ª´M²šÀ±Ô€ äøqÄ
>
W¯]œ|ñ=(RA*9yïå9}¡ˆ€Q$H¨ì*¡Ñ‘£ýH"8HJ£nÇáƒER`ßËýuìûµ1NnÉ·©!ÀR0~‚@¥ú;1¾Œ$|š {ë‚ü,©ÎÄ³8±K·ráiù9¶Èá!Ãó’(éÀ€Î	ŒÓ#YËB9UÄ{8"u&)Á‰¾Ões&¾¹þp^O"åiÊ\ÚÑ½9gŠ¥°õÂêXü…4Ž²)Æ ¿ÐIm¬‚¦6VA$NIô¦M:/÷ž}s‚¬—¾Ž /H‡Ö…ÎyÑ;#’ææÆŸsž‰L”÷Ò¯Ž¨ÇÆÇ4÷_›|Kþ+É¿¡ç=Y.'bw¢à>Õ’Ò	_ÀYú*ÕÖÿLÜùØ8Â<ÄØSÔ$­“O“Ó2ð•sî¶ÜÝ{>@&oO­—/ã¬ŸN¸æÂ{àHTïì’qçEXò>”~ÖE¡RÇÎÃ@¸üäA~ü.äé=6Ž„ßÉ‚ÛÁá²6î@}
*[„gð¬1i†ÄÌ6c_:¿¢§ZBô@´¶Jì–ùDÌZ0}ÇÙ¸·ì¾.»Ï­«¼ã o7‰úçòÊ]]F¸óœÄó@¢Æ?fÃž`ü s­'°.žõEÕhÕŠó£ôÖG¡Ê˜Œ¶ù{üYÃ;—U¥KÛRäº¹È:)–ZÓ/´­„í·èŠ7â««P›múAõŸ²»¶l¿šÛÝ9d_ÊÎ±‰è©k?¼Øþ[bÍb@UÎZ—0ÎOµ9ž+=™néu+á¯¼Îûá#1ç¹wáµ;ŸÌ éæJ•À‡SÞ} 4
lÄŠîaùxÀ7F‹sÞË¥œ|„sÙ{—Ây¡Å•ø„Ê:ž–v”4šV}pìÝÌ,òH]ÀOŒUF"C2IË%c™œÆ¶ì«¿Ïê(›Mhþ€ÑkE¾—‚æääñe”¨»?ÿòü.q³4ß«9ü<\Æ‰Ãz¬]-	:üÞÎ¸ÓÜááÝ¡G"¡7ß­*…¥‡å¨ -,%tru™Á†ñ€¾m¢†yÇÌXMåLC\¯§eÿ!gF£\–%J Çmðåb’?#.‡jSD·áÕº¦±;”^$mã¯èIË?K¦öM‡Ìek®¨@Ôãó·ãóc6!ðNüKgì4›±¼ÎkÊ¯)òkú¼ñ?:Í>ÌuÄC¤gó²U*­
· ‡Í¹à¢){@¥uûá^	®ËS‹B{Ù£H}Rˆ{ c§”p¬Ú` RßÏ\ûšå¸PÑÿÁHð%Ð÷Š\Å¸Àj™äÖC&Ï@8í´qDxuçƒ³­¿ÔpÂù\.¨E‹V~!èäôädä‹:d~ß¼{øÓ·?ú–­ùÔä½U¿pOjÇ8ŠÇ{®º˜ŒW„Ä¢LcQÎTÁÿûÞ«Ú_{UµrM§mª)1Îƒ¤HNl“ÆÍÎ’bR‹¡¯ñ#5£½mGuâ—ÂàÂ¬88œ„'µÇñýV´66.‹£–öÄÁ‹æ‘—YÞ>’³ˆŸ5;YjÅqf?ÉÛNŒ­±6ßp$ßœ.ÏH{êi»(“°&ð0ò“0áÚÔ/’úšï]àQà/¬sÕr{µ/U¹MµÆ†bÜt9\&uÎ5üyq¹A0ð TP“s˜ÑìíOVeMÎŠ^R:×§à]STQ#SLÚl™Á1[’ŒÀvYÖÓñâÑõarñÐ{pïSãø= ‰ø’CƒW8·Ñ ½ƒ©Tˆä«·DôäÎZWM©y+#j@;ì§
¾ãºçÒý%¾WÏg(XâÆë@ÖOý@¨[TÜÌ‡â%îâI#Z5Ëvèý	¶«Ò”C^òMùhÒ« 0Îwûi£H·gú²ˆ|/GFt]S²|A#Ë(¶ŒFb.k¦Æøµ‹[}Dë¥A…g>Ã‹‡vÎ˜Zý¬ëjjúè¦ö ìåÈwä±æõåS]õó^à¶|[„[SÆ´Ÿ.YùW HO5Üd§)k@ê\7	Ï‚á1ëÐsGž·ïhéØ~*{rº,áT©ŠÖMµh»O»üJ:_ö.Çíôeœê?Ñ–U÷ÃªHëçÙ‘á&ˆïq5¼oztð?Ì(ðçkoÁ©´Å{H÷¦Ò†:fê)õÝ tP-%wG-–í§‹HŸãÍ&Ü8ïhG‡ß‡ˆÔ.ªÄï%š’º³óü"ia™€›ä9ª÷ãŸÅ’J]œŠžkóÓáâ5áO•‰¬¶,½–úÙßïÝAz­|–FôŽ{¿V§†RH×šÅ(õ®ñ9æR,ÍÔ6T.Ó¼GÕÐsÂ+L°Ü,ü:aìàHªÔÐ‡/Šù~ë3'uü˜#Ý]½XsØ½H@~RkßG¤ÎTBínay§ç¾®òV#ñ®®MÝ½G@kLuÝT.ra!;éáqõre¬Û¢w}ôdæ—Ço¼<Ö ¨·ÐÝ‚á­fŒñTî5Ìî:F·Åíæ&w°õ°Xon³Ûá™ZX–¸‰™µýëLl‹ÕÍÌë¦PulbVæñ•Êªº…½Ï6*SmdS[ ndR7Â©Eàj xhåx[šÝoiv)p—!²)ŽÛ*í^«´p¼VÖƒ‹âýl›ãg™âÂ{©Ý64ÃÏ1Âmip·eÜfÆ·AqÐ‡zÛVtÜµ`Ûww\Ümjj›WÛ:ïŽë¼ílfQz4øø^ì¹ÄæSí”­d¢†–ö¹ä-±Ú×yh+	Ým](¿b+“D“º)ËþëôRìDšÞéÊ…Ã¥ ~Qßïf»ºÿºÖéÛÝ×ÜÏüv@áù}¾èwßÕj
íx—Œ¦XGíjMµÓî+Ó—æwQõÛ à†*qm¯´]mMé(­í{ÛòçNùóHØs-îp°åÍò†u¼g.aJ$î:œ¹)çdµ¶®ä‹©¶t¹cº˜ê¡³%"ÁÕ‰ÚËëù}0¥ƒýÜ—¶f£ÔðFzÐ;#:¦·üvïvõ]f™ýBþÕ‡Úµñ# ¶®mŒ6MõÛ±nñ½O|eÂ
€³ ýŽ‡/Å.Ãa«Î’¼±z¦ÐÖ®¨–´Õv™/ž3j¡Ö,¾ç=ê¢uMØ•*’Å[ÉS©Ä»Uù~Ø©ÊÃXQGäH¼;Óþ?#ˆD®n€6ª]¾Xâÿ¯ƒÞ]Ã°§Wxe¿sÉÍÝ‘¨æt!Õ©µÛoÃéü®Á`1'GürüXÑÀHQŽ^öÁÃð÷‘¸±ÁƒúM>c .ÚºwÉüvÓÞØ‡Ÿß¾÷‚={—"6x¯eëFÿnôM®ˆK¯k    0707010005c82a000081b40000000000000000000000015ab2175400000eae000000660000000000000000000000000000003c00000000root/usr/share/doc/opensvc/template.service.fs.ext2.conf.gz   ‹      í]ksÛ6ýž_™LÆÎ¬¤µ³Íç1õn“Ì¶MgÝf?t:6D‚Ö$Á du»ÿ}Ï½ )P~HŽ±»ÒL[$/Á{Î}—ÈÓ§·ùyòTÜâ‡Äev Ü¢RB¹7w‹ˆ»]Ý=ù9³O÷~yòŠŸ÷?ñ“'4êSµ˜›:=XÞ<©•têØTN›Òâ”á-~HO61•çª»çu£è@­~mt­–ƒy/sËGªÚÌ´Åxt99ˆ/IU&›Ü-‡ÿ½)ù€.§ªÖN–I¸O®d&ÞŠ©’i;%ðäªöÇm¥éçLT‰k:-1åLÕ‘|;ÍÕk÷¶	¦©&UÉ\	gD%-ÿë¦Jäf¢ž™¼)TP0D…,ÓQD#ý›øN”çÉrœœ
«Î{òª€1B§'i¬X\élòcÖ€ƒ•k2†&ÕöTLjÓTük¦seÖ©Ìt¢€·iòTŒUS÷*Ym
†f6™1—a`íE¡=ð€l‰U²‚}Ù¢³b8dVíà¡êçha6‡J,HUµ²|Î]¾}öŸ÷ÿ|÷î÷>ýþéïÿeäøÄ+±&teÝ«s“œ¶Bê'ÝF¦£9	¦Æ:–ÐŒ ‰Î¦…iJ7ßè,Sµ*]+9‘%ìŽ|Ÿ€yÁ´=#UL46Ü„®ýš¾c#·‹ÒIòÖ¯ðõÕÿæõõå0¾ÜI~I!ÞU²c´1”O1m$>d„Ê =Â ŠÊhü­mN­*IxÁOâ˜†$·’n*jc\fÙù^WÂS0è_f±Æç¯:;Z	c¬^(o­v)§ØòÿœŠC¦Õª‘´´&Ì—²:þâ±þ!)óC¶§á$Ä¸ÑH«àÔ%«Œk5Ü#¨ô¼I)ž©|±¼`÷ßTÃÍÎèïÑhôÜß®‰cŽA¶Qk
ýèž\ìï=khlœÀšžWóÞnˆDÐñÁÑ#Î3Úƒ¸µÏÏ‘.úA†0ÎTÔˆDÉ‹/ú	€YÓÔ‰²«ÂšR³ ïÔDŽNyæu¤ºš{¥9F¶£ŠÊË±©Ÿ=wGn+ãD-¥S b–£¿/ïs.%ƒ[•«¦œØq¸~g >Vª<úô71×y.‚Ê1&«	XUÏ$9E”“½ºÁŸ*?<%‡ê$”ÝA:ÀÁ‘82”Çh'ë‰ŠS]T¹*^¥¯S`MUáRo¹´®,…­({ö|´|˜HÐNFúØ!ƒcÚwVÉ´Óæc22ËÙz+U{!ß3[Œ¥Õž½«’@bÀ%4®&ÇIÕ<Ô™‡Ït ×`mª@¯Ô.‡ª¿š@´ø™ÆüK"ÏÍ\ñ(mÆˆáXxòa¬KB	.“½˜¿€#õŽÄßð7M2Ä¾¬;E‚;Ü'jY"?{ƒýÁúboø‚p8Núâr0UlÁ¼;0¡^S/|!O&}}ÏsI×—p[.pl§¿…øjˆ/6ÈÞÕDÕ=øÿ¡êRå]Ž2“9"–¦
W¥äú:™âçFæà¾Þß{ñ•ðú‡o/{áÐÇÈbS¤-®á 1ª´M²šÀ±Ô€ äøqÄ
>
W¯]œ|ñ=(RA*9yïå9}¡ˆ€Q$H¨ì*¡Ñ‘£ýH"8HJ£nÇáƒER`ßËýuìûµ1NnÉ·©!ÀR0~‚@¥ú;1¾Œ$|š {ë‚ü,©ÎÄ³8±K·ráiù9¶Èá!Ãó’(éÀ€Î	ŒÓ#YËB9UÄ{8"u&)Á‰¾Ões&¾¹þp^O"åiÊ\ÚÑ½9gŠ¥°õÂêXü…4Ž²)Æ ¿ÐIm¬‚¦6VA$NIô¦M:/÷ž}s‚¬—¾Ž /H‡Ö…ÎyÑ;#’ææÆŸsž‰L”÷Ò¯Ž¨ÇÆÇ4÷_›|Kþ+É¿¡ç=Y.'bw¢à>Õ’Ò	_ÀYú*ÕÖÿLÜùØ8Â<ÄØSÔ$­“O“Ó2ð•sî¶ÜÝ{>@&oO­—/ã¬ŸN¸æÂ{àHTïì’qçEXò>”~ÖE¡RÇÎÃ@¸üäA~ü.äé=6Ž„ßÉ‚ÛÁá²6î@}
*[„gð¬1i†ÄÌ6c_:¿¢§ZBô@´¶Jì–ùDÌZ0}ÇÙ¸·ì¾.»Ï­«¼ã o7‰úçòÊ]]F¸óœÄó@¢Æ?fÃž`ü s­'°.žõEÕhÕŠó£ôÖG¡Ê˜Œ¶ù{üYÃ;—U¥KÛRäº¹È:)–ZÓ/´­„í·èŠ7â««P›múAõŸ²»¶l¿šÛÝ9d_ÊÎ±‰è©k?¼Øþ[bÍb@UÎZ—0ÎOµ9ž+=™néu+á¯¼Îûá#1ç¹wáµ;ŸÌ éæJ•À‡SÞ} 4
lÄŠîaùxÀ7F‹sÞË¥œ|„sÙ{—Ây¡Å•ø„Ê:ž–v”4šV}pìÝÌ,òH]ÀOŒUF"C2IË%c™œÆ¶ì«¿Ïê(›Mhþ€ÑkE¾—‚æääñe”¨»?ÿòü.q³4ß«9ü<\Æ‰Ãz¬]-	:üÞÎ¸ÓÜááÝ¡G"¡7ß­*…¥‡å¨ -,%tru™Á†ñ€¾m¢†yÇÌXMåLC\¯§eÿ!gF£\–%J Çmðåb’?#.‡jSD·áÕº¦±;”^$mã¯èIË?K¦öM‡Ìek®¨@Ôãó·ãóc6!ðNüKgì4›±¼ÎkÊ¯)òkú¼ñ?:Í>ÌuÄC¤gó²U*­
· ‡Í¹à¢){@¥uûá^	®ËS‹B{Ù£H}Rˆ{ c§”p¬Ú` RßÏ\ûšå¸PÑÿÁHð%Ð÷Š\Å¸Àj™äÖC&Ï@8í´qDxuçƒ³­¿ÔpÂù\.¨E‹V~!èäôädä‹:d~ß¼{øÓ·?ú–­ùÔä½U¿pOjÇ8ŠÇ{®º˜ŒW„Ä¢LcQÎTÁÿûÞ«Ú_{UµrM§mª)1Îƒ¤HNl“ÆÍÎ’bR‹¡¯ñ#5£½mGuâ—ÂàÂ¬88œ„'µÇñýV´66.‹£–öÄÁ‹æ‘—YÞ>’³ˆŸ5;YjÅqf?ÉÛNŒ­±6ßp$ßœ.ÏH{êi»(“°&ð0ò“0áÚÔ/’úšï]àQà/¬sÕr{µ/U¹MµÆ†bÜt9\&uÎ5üyq¹A0ð TP“s˜ÑìíOVeMÎŠ^R:×§à]STQ#SLÚl™Á1[’ŒÀvYÖÓñâÑõarñÐ{pïSãø= ‰ø’CƒW8·Ñ ½ƒ©Tˆä«·DôäÎZWM©y+#j@;ì§
¾ãºçÒý%¾WÏg(XâÆë@ÖOý@¨[TÜÌ‡â%îâI#Z5Ëvèý	¶«Ò”C^òMùhÒ« 0Îwûi£H·gú²ˆ|/GFt]S²|A#Ë(¶ŒFb.k¦Æøµ‹[}Dë¥A…g>Ã‹‡vÎ˜Zý¬ëjjúè¦ö ìåÈwä±æõåS]õó^à¶|[„[SÆ´Ÿ.YùW HO5Üd§)k@ê\7	Ï‚á1ëÐsGž·ïhéØ~*{rº,áT©ŠÖMµh»O»üJ:_ö.Çíôeœê?Ñ–U÷ÃªHëçÙ‘á&ˆïq5¼oztð?Ì(ðçkoÁ©´Å{H÷¦Ò†:fê)õÝ tP-%wG-–í§‹HŸãÍ&Ü8ïhG‡ß‡ˆÔ.ªÄï%š’º³óü"ia™€›ä9ª÷ãŸÅ’J]œŠžkóÓáâ5áO•‰¬¶,½–úÙßïÝAz­|–FêlµV§†RH×šÅ(õ®ñ9æR,ÍÔ6T.Ó¼GÕÐsÂ+L°Ü,ü:aìàHªÔÐ‡/Šù~ë3'uü˜#Ý]½XsØ½H@~RkßG¤ÎTBínay§ç¾®òV#ñ®®MÝ½G@kLuÝT.ra!;éáqõre¬Û¢w}ôdæ—Ço¼<Ö ¨·ÐÝ‚á­fŒñTî5Ìî:F·Åíæ&w°õ°Xon³Ûá™ZX–¸‰™µýëLl‹ÕÍÌë¦PulbVæñ•Êªº…½Ï6*SmdS[ ndR7Â©Eàj xhåx[šÝoiv)p—!²)ŽÛ*í^«´p¼VÖƒ‹âýl›ãg™âÂ{©Ý64ÃÏ1Âmip·eÜfÆ·AqÐ‡zÛVtÜµ`Ûww\Ümjj›WÛ:ïŽë¼ílfQz4øø^ì¹ÄæSí”­d¢†–ö¹ä-±Ú×yh+	Ým](¿b+“D“º)ËþëôRìDšÞéÊ…Ã¥ ~Qßïf»ºÿºÖéÛÝ×ÜÏüv@áù}¾èwßÕj
íx—Œ¦XGíjMµÓî+Ó—æwQõÛ à†*qm¯´]mMé(­í{ÛòçNùóHØs-îp°åÍò†u¼g.aJ$î:œ¹)çdµ¶®ä‹©¶t¹cº˜ê¡³%"ÁÕ‰ÚËëù}0¥ƒýÜ—¶f£ÔðFzÐ;#:¦·üvïvõ]f™ýBþÕ‡Úµñ# ¶®mŒ6MõÛ±nñ½O|eÂ
€³ ýŽ‡/Å.Ãa«Î’¼±z¦ÐÖ®¨–´Õv™/ž3j¡Ö,¾ç=ê¢uMØ•*’Å[ÉS©Ä»Uù~Ø©ÊÃXQGäH¼;Óþ?#ˆD®n€6ª]¾Xâÿ¯ƒÞ]Ã°§Wxe¿sÉÍÝ‘¨æt!Õ©µÛoÃéü®Á`1'GürüXÑÀHQŽ^öÁÃð÷‘¸±ÁƒúM>c .ÚºwÉüvÓÞØ‡Ÿß¾÷‚={—"6x¯eëFÿnôÖá:Ù°k    0707010005c82b000081b40000000000000000000000015ab2175400000eae000000660000000000000000000000000000003c00000000root/usr/share/doc/opensvc/template.service.fs.ext3.conf.gz   ‹      í]ksÛ6ýž_™LÆÉ¬¤µÓæ‹ó˜zÛd'³MÓY·ÙŽ‘ „5I°(YÝîßs/@
”’ãGì®4“ÄÉKðžs_À%òøñM~=7ø!q™·¨”P§î«ë‹»ÁÄÝ¬îý’ÙÇ»¿>zÉÏûšŸøÑ#õ‰ZÌMî/ožÔJ:ud*§MiqÊð?¤'›˜JŽsÕÝó§ºQt V¿5ºVËÁ¼“¹å#UmfÚb<ºœìÇ—¤*“Mî–ÃÿÁ”|@—SUk'Ë$Ü'W2oÄTÉ´„xrUûã¶R‰Ît‚s&ªÄµ	–˜r¦êH¾æê”5‡{ÛÒT“ªd.‚Î„3¢’–ÿuS%r3Ñ	ÏLÞ*(¢‹B–é(¢Î¡þ]	|'J‰ód?9NN…Uç=zÙÇ@‡¡Ó“Æ4V,.‚t6ù¿†1ë'ÀÁÊ5C“j{"&µi*þ5Ó¹²ëTf:QÀÛ4y*Æ*‚©{•Š¬6C3› ŽÌ˜‹0°Àö‹¢Ð¸G¶Ä*YA‡¾lÑY12«vðPus´0‰ƒ%¤N«ZY¾ ç¿*ß<ùÏ»¾}ûÇŸÿøô÷ÿ2r|â¥Ø÷ºÎ²îÔ€Æ¹INZ!õ“n#ÓÑ…œ„ScKhFÐDçÓÂ4¥‰ït–©Z•®•œÈvG¾OÀ<`Úž‰*&nB×~Cß±‘ÛEé$yë—øúrˆ÷úúr_ì$¿È¤ï*Ù1ZŒÊ§˜6ï3BeÐa Ee4þÖ6§V•$¼à'qÌC’[I7µ1.³l|¯Ká)ô/‡Î=³ÀXãsˆW­„1V/”·V»”SlùFÅ!ÓjÕHZZæKY}ñXŸ”ù>[ÓpbÜh¤Upê’UFµîTzÞ¤”NÏT¾X^ðôßTÃÍNéïÑhôÌß®‰cŽA¶Qk
ýèž\ìí>ihlœÀšžWóÞnˆDÐñÁÑ#Î3Úƒ¸µÏÏ‘.úA†0ÎTÔˆDÉ‹/ú	€YÓÔ‰²«ÂšR³ j"Ç§<ó:R]Î½Ò!ÛQEåŽäØÔÏž»#7•q¢–Ò)±ûËÑŠ?–÷9“’ŽÁ­ÊUSNì8\¿3+U~úVÌuž‹ rŒÉjVÕ3INGåd¯nðg†ÊO	Æ¡º	ewðBp$å1$ÚÉz¢âÔGU®
„Wé«ÁÔ€GSU¸ÔÛB.­ƒ+Ka+Êž>-&´“‘>vÈàX …öU2í´ù˜L`†Ìr¶ÞJÕÃ^È÷Ìciµgïª$Øp«ÉQR5÷uæá3èX›*Ð+µKÁ¡ê¯&-~¡1ÿÚŸÈs3D<J›1b8ž|ë’P‚Ëd/æïàH½#ñ-þ¦I†Ø—u§HpGƒ;àD-K¤óûbw°7xN_ìŸ®'ÀI_\f¡Š-˜·&Ôkê…/„âÉ¤Ñ÷<—tUp	·µà‚Gv
ñ[ˆ/‡øœ`ƒì]MTÝƒÿª.UÞå(3™#biªpU:@®¯“)~ndàë½Ýç_¯øö²n ÍpŒ,6EÚâ¡JÛÔ!«	KBŽ÷@¬à£põÚÅÉßƒ"¤’“÷^žÓŠE‚ÑˆÊþ Š9Ú$‚ƒ„ ´Á0êv>Xô(ö½Ø[Ç¾ßãä–|›ú, ã)Tª¿ãËHÂ'¡	êP°·.ÈÏ’êL<‰s»t+ ž–¯‘c‹2<!‰’èŒÀ8=’µ,”SõH¼ƒ#R§’œ‘è{]6§"á+ë÷àõ$Rž¦Ìu¡Ý›s¦XšëQ/¬ŽÅ_Hã(›búáñÔÆ*hjcDâ”„áAoÚ¤#ñb÷É78'Èzáëú‚thýXèœç½3"innü¹1ç™ÈDy/ýòˆzdLqDsÿµÉ·ä¿”üzÞ‘år"žîRÜ£ZR:á8K_¥ÚúŸ‰;G˜‡{‚:€¤uòirZ¾rÎÝ‚OwŸÉÛëåKç8ëç‚®¹ð8Â;»dÜy–…¼¥ŸuQ¨Tã±ó0.?y?D‚ü ½ÇÆ‘ð;Yp;8\ÖÆ¨OAe‹ð~‚5&0Íø‘ÙfìKç—ôAKˆˆVÀV‰½Ñ2ŸˆY¦ï­á8÷–ÝWe÷™u•·ôí&QÿL^ùT—î<'ñ,¨qÃÙ°g?È\ë	¬‹g}Q5Zµâü(} õQ¨2æ£íCþ.Ö°ÃÎeUéÂ¶¹*EÎs€‡€NŠ¥VCÀôm+a;Æ-ºâµøú2Ôf[£¾Fý‚ì¶-Û¯ævwÙWƒ²s¬@"zêÚ/¶ÿ–X³Ø#P•³Ö%ŒómŽæJO¦[zÝˆCø¯ó¾ÿˆGÌyî]xíŽÄ's@º¹R%ðá”w B±¢{X>ðÑâœ÷"dA)'à\öî…pÞ@çÄyhq%>¡²Ž§¥%¦U{73‹<Rðc•‘‡ÈLÒrÉX&'±-ûê¯À³:Êfš?`ôZ‘¯Å… 99yxA%êÓ_~}v›¸YšÀïÕ~.cÄA=Ö®–~ogÜiî‡ððîÐ#†Ð›ÇïV•ÂÒÃrT–:¹ºÌ`Ãx@ß6QÃ¼ã
f¬¦r¦!®WŒÓ²€¿ÿŽ3£Q.Ë%€ã†6øò	1ÉŸ—Cµ)¢Ûðê]ÓØJ/’6ŠñWô¤å_%SGûºCæ¢5WT êáyˆ›ñù1›x'~Ž¥3všÍX^ç5å×ùÇ5}Þøœfïç:âÒ³yÙ*•V…[‚ÃæÜ
pÑ”= Ò‰Æ¿:‹ýp¯×å‰E¡½ìQ¤>)
Ä=±SJ8Vm0P©ïg®}Ír\¨èÿd$øè{E®â\`µLrë!“g œvÚ8¢N¼ºóÞÙÖ_j8á|.Ô¢E+¿t|z|<òE2¿ïÞ¾;øùûŸ|ËÖ|jòÞª_¸'µcÅã=S]LÆ+BbQ¦±(gªàÿ}oƒUí¯½‰ªZ¹¦ÆÓ6	Õ”ç~R¤ûÇ¶Iã€fgI1©ÅÐŠWø‘šÑÞ´£:öKapaVìŸÌ÷ÃŽÚãx‚~+Z—ÅQKûGâàEóÈË,oÉYÄÏš/µâ¿8³Ÿäm'ÆÖX›o8’oN—‚ç
¤=ñ´]”IX¸ŸùGƒ€I˜pmêI}‹Í÷†®ð(ðÖ¹j¹½Ú—ªÜ¦ZcÃ1nº.“:çþ¬¸Ü øN*¨É9Ìhöög«²&gE/)ëð®)ª¨‘)&m¶Ìà˜‹-É@Æ`»(ëŠéxñàˆz?¹xà=¸÷©qüÐD|É¡Á+œÛ†hÐÞÁŠT*DòÕÆ["zrg­«¦Ô¼•5 ôSßqÝséþß«ç³F,qãu kŒ§~ Ô-*næCñ÷ñ¤­‚e;ôþÛUiÊ!/ù¦üF4éUPç;Œý´Q$‡Û3}YD¾—Œ#£Æ	º®)Y¾ ‘e[F#1—5Sc_|‡ÚÅ­>¢¿õÒ Â3ï‡áÅC;cL­~Öu5µ}pÓ
»Wörè;òØGóúò‰®úy/p[¾-BŽ­)cÚO—¬ü+P¤§n²Ó”5 u®›„gÁð˜uè¹#ÏÛw´…tl?—=9]–p¢TEkƒ¦Z´Ý§]~%/{—ãöú"NõŸhËª»aU¤õ³ì‰ÈpÄ÷¸œÞ7=8øïgøóµ7àTÚâ=¤{SiÃ3õ”ún:¨‹–’»ÃËöÓE¤Ïñfnœw4ŽÃƒBDjUâ÷MIÝYy~ž´°LÀMòÕûñÏ„âI¥®NEÏ´ƒùi„pñšð§ÊDV[–ÞKýìï÷î ½V>K£ŽuºZ«SÃ‰N©¤kÍb”z×øs)–fj*—iÞ£jè9á&Xn~0vp$UjèÃÅ|¿õ™“:zÈ‘î¶^¬9è^$ ?©µï#R§*¡v·°¼Ós_—y«‘x[×¦îÞ# 5¦ºn*¹°ôð¸|
¹2ÖmÑ»:z2óËã7Þ
kÐÔ[ènÀðV3Æx*÷
fw£Ûâv}“»	ØzX¬7·‡ÙípL-,K\ÇÌÚþ†u&¶Åêzæu]¨:61+óðÊ{eUÝÂÞg•©6²©-P×2©káÔ"p9P¼´r´-Íî¶4»¸‹ÙÇm•v§UÚ8^©NëÁ¿Eñn
¶ÍŒñ³LqáÔnšáçá¶4¸Ý2n3ãÛ 8èC½…íV+ºîJ°m‹»[.î65µÍ«†mwËuÞÆv¶³(=
|x/öÜbó©vÊV2QCKû\ò–Xíë<´•„î6ƒ.”_±•I¢€IÝ”eÿuz)v"MïtåÂÁR¿¨ïw³]]ŒUëôÍÓWÜÏüf@áù}¾èwßÕj
íx—Œ¦XGíjMµÓî+Ó—æwQõÛ à†*qm¯´]mMé(­í{ÛòçVùó@Øs%îp°åÍ­ò†u¼g.`J$î*œ9—)gdµ¶.å‹©¶t¹eº˜ê¾³%"Áå‰ÚËêù]0¥ƒýOÜ—¶f£ÔðFzÐ;#:¦·üvïvõ]f™ýBþÕ‡Úµñ# ¶®mŒ6MõÛ±nñ½K|eÂ
€³ ýŽ‡/ÄS†1ÂV&ycõL ­	\Q-i«í2_<cÔ¾¢Ö,¾ç=ê¢uMØ•*’Å[ÉS©Ä»Uù~Ø©ÊÃXQGäH¼=Õþ?#ˆD®n€6ª]¾Xâÿ¯ƒÞ]Ã°§Wxe¿sÉÍÝ‘¨æt!Õ©µÛoÃéü®Á`1'GürüXÑÀHQŽ^öÁÃð÷‘¸±ÁƒúM>c ÎÛºwÉüvÓÞØ‡ŸÝ¾÷œ={—"6x¯eëFÿnôæòU°k    0707010005c82c000081b40000000000000000000000015ab2175400000eaf000000660000000000000000000000000000003c00000000root/usr/share/doc/opensvc/template.service.fs.ext4.conf.gz   ‹      í]ksÛ6ýž_™LÆÎ¬¤µ³Íç1õn“Ì¶MgÝf?t:6D‚Ö$Á du»ÿ}Ï½ )P~HŽ±»ÒL[$/Á{Î}—ÈÓ§·ùyòTÜâ‡Äev Ü¢RB¹¯n.î?w»º{òsfŸîýòä?ï~â'OhÔ§j17uz°¼yR+éÔ±©œ6¥Å)Ã[üžlb*9ÎUwÏëFÑZýÚèZ-ó^æ–Tµ™i‹ñèrr_’ªL6¹[ÿ{Sò]NU­,“pŸ\ÉL¼S%ÓvJàÉUíÛJ%:Ó	Î™¨×&tZbÊ™ª#ùvš«3Öîm8LSMª’¹:ÎˆJZþ×M•ÈÍD'8<3yS¨ `ˆ.
Y¦£ˆ:Gú7%ð(%Î“5þä89V9œ÷äU 9b„NOÓX±¸ÒÙäÿÆ¬+×dMªí©˜Ô¦©ø×LçÊ.¬S.˜éDoÓä©«¦î1T*²ÚÍl82c.ÃÀÛ/ŠB{àÙ«dú²EgÅpÈ¬ÚÁCÕ5ÎÑ6Âl$#”X:«jeùœÿº|ûì?ïÿùîÝï?|:úýÓßÿËÈñ‰Wb.<Lè:ËºWç&9m„ÔOºLGrLu,¡ALÓ”n$¾ÑY¦jUºVr"KØù>ó‚i{F$ª4˜hl¸	]û5}ÇFn¥“ä­_áë«!þÍëëËa|¹“ü2 “B¼«dÇh1b(ŸbÚH|È•A{„•Ñø[ÛœZU’ð‚ŸÄ1In%ÝTÔÆ¸Ì²ò½®„§`Ð¿:ÌcÏ!^uv´ÆX½PÞZíRN±åÿ9‡L«U#iiM˜/euüÅcýCRæ‡l5NÃIˆq£‘VÁ©KV=Öj¸GPéy“R:=SùbyÁî¿©†›Ñß£Ñè¹¿\Çƒl£ÖúÑ'<¹Øß{Ö&ÑØ8€5!=¯ç½Ý‰ ãƒ#¢Gœg´qkŸŸ#]ôƒ"aœ©¨‰’!_ô ³¦©eW…5¥fAß©‰/œòÌëHu5÷JsŒlG•;–cS?>{îŽÜVÆ‰ZJ§@Ä,G+~_Þç\J:6·*W	L98±ãpýÎ@|¬Tyôéob®ó\•cLV°ªžIr8Š('{uƒ?3T~xJ0ÕH(»ƒt€ÿ‚#qd(!ÑNÖ§>º¨rU ¼J_¦À8šªÂ¥Þri\Y
[Qöìùhù0‘ Œô±CÇ(´ï¬’i§ÍÇd3d–³õVªöB¾g¶K«={W%Ä€Kh\MŽ“ªy¨3Ÿé@¯ÁÚT^©]
U5hñ3ù—þDž›¹ âQÚŒÃ±ðäÃX—„\&{17 Gê‰¿áošdˆ}YwŠw4¸NÔ²D: öûƒôÅÞðápœôÅå`ªØ‚yw`B½¦^øB(žLú.úžç’®.á¶\0àØN!~ñÕ_l½«‰ª{ðÿCÕ¥Ê»e&sD,M®JÈõu2ÅÏÌÁ|½¿÷â+áõß^öÂ Ž‘Å¦H[\ÃAb Ti›:d5c©AÈñãˆ|®^»8ùâ{P¤‚TròÞËsúB¢H QÙTB¢#Gû‘Dp”6FÝŽÃ‹¥À¾—ûëØ÷kcœÜ’oSÿB€¤`ü Jõwb|Iø$4A
öÖùYR‰gqb—näÂÓò5rl‘ÃC†ç/$QÒ§G²–…rª‰÷pDêLR‚3 }«ËæL$|rýá>¼žDÊÓ”¹.´£{sÎK3`=ê…Õ±øieSŒA?<~¡“ÚXMm¬‚Hœ’0<èM›t$^î=ûçY/}A_­ó¢wF$ÍÍ?7æ<™(ï¥_Q)Žiî¿6ù–üW’CÏ{(²\NÄîEÁ}ª%¥¾€³ôUª­ÿ™¸ó±q„yˆ±§¨HZ'Ÿ&§eà+çÜm!¸»÷|€LÞžZ/_:ÇY?œpÍ…÷À‘¨ÞÙ%ãÎ‹°,ä}(ý¬‹B¥‡pùÉƒüø]$ÈÒ{l	¿“·ƒÃemÜúT¶Ïà'XcÓ‰™mÆ¾t~EO´„èhl•Ø-ó‰˜µ`úþŽ³qoÙ}]vŸ[WyÇAßnõÏå•»ºŒpç9‰çD~Ì†=ÀøAæZO`]<ë‹ªÑªçGé­B•17mò÷ø³†v.«J—¶¥Èu)r‘<tR,µ¦_h[	Û1nÑoÄWW¡6Ûõƒ0ê?dwmÙ~5·»sÈ¾”cÑS×~x±ý·ÄšÅªœµ.aœŸjs<Wz2ÝÒëVÂ_y÷ÃG<bÎsïÂkw$>˜ ÒÍ•*§¼û h*ØˆÝÃòñ€oŒç¼—!J9ùç²÷.…ó:'.B‹+ñ	•u<-í(i4­úàØ»™Yä‘º€Ÿ«Œ<D†d’–KÆ29mÙWžÕQ6›Ðü£×Š|#.ÍÉÉãÊ(Qwþåù]âfi¿Wsøy¸Œ56‡õX»Ztø½q§¹ÂÃ»CDBo¿[U
KËQAZXJèäê2ƒã}ÛDóŽ+˜±šÊ™†¸^1NËþþ;BÎŒF¹,K” ŽÚàË'Ä$F\Õ¦ˆnÃ«tMcw(½HÚ(Æ_Ñ“––L5í›™ËÖ\Q¨Çç!nÇçÇlBàø9–ÎØi6cy×”_Sä×ôxãtš}˜ëˆ‡HÏæe«TZnA
›s+ÀESö€J'ÿê,öÃ½\—§…ö²G‘ú¤(÷@ÆN)áXµÁ@¥¾Ÿ!¸ö5Ëq¡¢ÿƒ‘àK ï¹Š;pÕ2!È­‡Lž
pÚiãˆ:ñêÎg[©á„ó¹\P‹­üBÐÉ	<èÉÉÈuÈü¾y÷þð§oô-[ó©É{«~ážÔŽq÷\u15¯‰E™Æ¢œ©‚ÿ÷½Vµ¿ö&ªjåšOÛ$TSbœI‘œØ&š%Å¤C+^ãGjF{ÛŽêÄ/…Á…Yqp:?8	O8jã	ú­hml\>G-í‰ƒÍ#/²¼}$g9?kv²ÔŠÿâ<Ì~’·[cm¾áH>¾9]6
ž+öÔÓvQ&aMàa:ä&aÂµ©_$õ-4ßº"À£À_Xçªåöj_ªr›jÄ¸ér¸Lêœkøóârƒ`à;A¨ &ç0£ÙÛŸ¬Êšœ½¤t®OÁ»¦¨¢F¦˜´Ù2ƒc.¶$7€í²¬(¦ãÅ£#êÃäâ¡÷àÞ§Æñ{@ñ%‡¯pn¢A{+R©ÉWo-ˆèÉµ®šRóVFÔ€vØO|ÇuÏ¥ûK|¯žÏQ°Ä×¬1žúP·¨¸™ÅKÜÄ“F´j–íÐûlW¥)‡¼ä›òÐ¤WAaœï0öÓF‘nÏôeù^2ŽŒ'èº¦dù‚F–QlÄ\ÖLñj·úˆþÖKƒ
Ï|†íœ1µúY×ÕÔ2ôÑL+ìAÙË‘ïÈcÍëË§ºêç½Àmù¶9¶¦Œ1h?]²ò¯@‘žj¸ÉNSÖ€Ô¹nžÃcÖ¡çŽ<oßÑÒ±	üTöätYÂ©R­šjÑvŸvù•t¾ì]ŽÛ;èË8Õ¢-«î‡U‘ÖÏ³'"ÃMßãjxßôèà˜Q8àÏ×Þ‚Si‹÷îM¥/t.ÌÔSê»Aè .ZJîŽZ,ÛO‘>Ç›M¸qÞÑ8Ž¿©]T‰ßK4%ugæùEÒÂ27ÉsTïÇ?/Š3$•º"8=×æ§ÂÅkÂŸ*YmYz,õ³¾ß»ƒ0ôZù,:.ÔÙj­N':¥®5‹Qê]ãsÌ¥Xš©m,¨\¦yª¡ç„W ˜`¹YøuÂØÁ‘T©¡_óýÖgNêø1Gº»z±æ°{‘€2ü¤Ö¾H©„ÚÝÂòNÏ}]å­Fâ]]›º{€Ö˜êº©\äÂBvÒÃãê)äÊX·EïúèÉÌ/ß:x+x¬APo¡»Ã[Íã©Ük˜ÝuŒn‹ÛÍMî6`ëa±ÞÜg·Ã2µ°,q3kûÖ™Ø«›™×M¡ê0ØÄ¬Ìã+?”Uu{ŸmT¦ÚÈ¦¶@ÝÈ¤n„S‹ÀÕ@ñ0ÐÊñ¶4»ßÒìRà.CdS·UÚ½Viàx­:­ÿÅû)Ø63ÆÏ2Å-„÷R»mh†Ÿc„ÛÒànË¸ÍŒoƒâ õ¶;­è64¸kÁ¶-îî¸¸ÛÔÔ6¯¶uÞ×yÛÙ:Ì¢ô8hðñ½Øs7ˆÍ§Ú)[ÉD-ísÉ[bµ¯óÐVºÛºP~ÅV&‰&uS–ý×é¥Ø‰4½Ó•‡KAü¢¾ßÍvu1þu­Ó·»¯¹Ÿùí€6Âóû|Ñï¾«ÕÚñ.;M5°ŽÚÕšj§ÝW¦/Íï¢ê·ÀUâÚ^i»ÚšÒQ$ZÛ÷¶åÏòç‘°çZÜá<`Ë›;åëx#Î\Â”HÜu8s!SÎÉj;l#\ÉSmérÇt1ÕCgKD‚«µ—×óû`Jû¸/mÍF©áô wFtLoøíÞíê»Ì2ú„ü«µkãG@m]Ûmšê·cÝâ{ŸøÊ„ gAû_Š]†1ÂV%ycõL ­	\Q-i«í2_<gÔþB­Y|Ï{ÔE;êš°+U$‹·’§R‰w«òý°S•‡=°¢ŽÈ‘xw¦ýF‰\Ý 5lT»|±Äÿ_½7º†aO¯ðÊ:!æ’›»#Q9ÍéBªSk·ß†Óù]ƒÁbNŽøåø±¢‘¢½ìƒ‡áï#qcƒõ›|Æ@\´uï’ùí¦½±?¿}ï{ö.Elð^ËÖþ!Üèÿ ôåjE°k   0707010005c82d000081b40000000000000000000000015ab2175400000eae000000660000000000000000000000000000003c00000000root/usr/share/doc/opensvc/template.service.fs.f2fs.conf.gz   ‹      í]ksÛ6ýž_™LÆÎ¬¤µ³Íç1õn“Ì¶MgÝf?t:6D‚Ö$Á du»ÿ}Ï½ )P~HŽ±»ÒL[$/Á{Î}—ÈÓ§·ùyòTÜâ‡Äev Ü¢R"{‘Ù›‹»ÅÄÝ®îžüœÙ§{¿<yÅÏû†ŸøÉõ©ZÌM,ožÔJ:ul*§MiqÊð?¤'›˜JŽsÕÝóÇºQt V¿6ºVËÁ¼—¹å#UmfÚb<ºœÄ—¤*“Mî–ÃÿÞ”|@—SUk'Ë$Ü'W2oÅTÉ´„xrUûã¶R‰Ît‚s&ªÄµ	–˜r¦êH¾æêŒ5‡{ÛÓT“ªd.‚Î„3¢’–ÿuS%r3Ñ	ÏLÞ*(¢‹B–é(¢Î‘þM	|'J‰ód?9NN…Uç=yÕÇ@‡¡Ó“Æ4V,.ƒt6ù¿†1ëGÀÁÊ5C“j{*&µi*þ5Ó¹²ëTf:QÀÛ4y*Æ*‚©{•Š¬6C3›]sØ~QÚÈ–X%+èÐ—-:+†CfÕª®qŽ¶f#q¡Ä‚ÔYU+Ëàü×åÛgÿyÿÏwï~ÿáÓÑïŸþþ_FŽO¼;páaB×YÖ½Ð87Éik ¤~Òmd:º“p`j¬Ã`	Íšè\`Z˜¦t#ñÎ2U«Òµ’YÂîÈ÷	˜'LÛ3"Q¥ÁÀDcÃMèÚ¯é;6r»($oý
__ño^__ãËä—™â]%;F‹CùÓFâCF¨Ú# ¨ŒÆßÚFàÔª’„ü$Žù`Hr+é¦¢6Æe–-ïu%<ƒþåÐy`k|ñª³£•0Æê…òÖj—rŠ-ÿÏ©8dZ­IKkÂ|)«ã/ë’2?d«qNBŒ´
N]²Êè±VÃ=‚JÏ›”Òé™ÊËvÿM5ÜìŒþFÏýàš8ædµ¦ðÐ>áÉÅþÞ³6ÑˆÆÆÙ ¬	éyÕ8ïýè†H=â<£=ˆ[ûüé¢äaãLEH”¹ø¢Ÿ ˜5M(»*¬)5úNMäxá”g^Gª«¹Wšcd;ª¨Ü±›úñÙswä¶2NÔR:"ö`9Zñûò>çRÒ±1¸U¹J`ÊAÀ‰‡ëwâc¥Ê£Osç"¨c²š€UõL’ÓÀQD9Ù«ü™¡òÃS‚q¨î@BÙ¤ü‡‰#Cy‰v²ž¨8õÑE•«áUúj05` ÆÑT.õ¶KëàÊRØŠ²gÏGË‡‰íd¤28@¡}g•L;m>&˜!³œ­·Rõ°ò=³ÅXZíÙ»*	$ö \BãjrœTÍCyøLzÖ¦
ôJíRp¨ú«	D‹ŸiÌ¿ôg òÜÌÒfŒŽ…'Æº$”à2Ù‹ù»8RïHüÓ$CìËºS$¸£Áp¢–%Òù±7Ø¼ /ö†/×€à¤/.³PÅÌ»ê5õÂBñdÒwÑ÷<—t]p	·µà‚Çv
ñ[ˆ¯†ø‚`ƒì]MTÝƒÿª.UÞå(3™#biªpU:@®¯“)~ndàëý½_	¯øö²n ÍpŒ,6EÚâ¡JÛÔ!«	KBŽ÷@¬à£põÚÅÉßƒ"¤’“÷^žÓŠE‚ÑˆÊþ Š9Ú$‚ƒ„ ´Á0êv>Xô(ö½Ü_Ç¾_ãä–|›ú, ã)Tª¿ãËHÂ'¡	êP°·.ÈÏ’êL<‹s»t+ ž–¯‘c‹2<!‰’èœÀ8=’µ,”SõH¼‡#Rg’œ‘è[]6g"á+ë÷áõ$Rž¦Ìu¡Ý›s¦XšëQ/¬ŽÅ_Hã(›búáñÔÆ*hjcDâ”„áAoÚ¤#ñrïÙ×8'Èzéëú‚thýXèœ½3"innü¹1ç™ÈDy/ýêˆzlLqLsÿµÉ·ä¿’üzÞC‘år"v÷(
îS-)ðœ¥¯RmýÏÄ#ÌCŒ=E@Ò:ù49-_9çnÁÝ½çdòöÔzùÒ9Îú¹à„k.¼ŽD…ðÎ.w^„e!ïCég]*Õxì<„ËOäÇï"A~ÞcãHø,¸.kãÔ§ ²Ex?Á˜fHüÈl3ö¥ó+zŠ %DD+`«Äþh™OÄ¬Ó÷×pœ{Ëîë²ûÜºÊ;úv“¨.¯ÜÕe„;ÏI<$jÜðc6ìÙ Æ2×zëâY_TV­8?Jh}ªŒ¹Áhû¿ÇŸ5ì°sYUº„°-E®K‘‹à “b©Õ0ýBÛJØŽq‹®x#¾º
µÙÖ¨„Qÿ‰ »kËö«¹ÝCöÕ ì+ˆžºöÃ‹í¿%Ö,öTå¬u	ãüT›ã¹Ò“é–^·âþÊë¼>âsž{^»#ñ‰À< n®T	|8åÝ@£PÁF¬è–|c´8ç½YPÊÉG8—½w)œ·Ð9qZ\‰O¨¬ãiiGI£iÕÇÞÍÌ"ÔüÄXeä!2$“´\2–ÉilË¾ú+ð¬Ž²Ù„æ½Väq)hNN_PF‰ºûó/Ïï7Kø½šÃÏÃe¬±‘8¬ÇÚÕ’ ÃïíŒ;ÍýÞz$ÂzóøÝªRXzXŽ
ÒÂRB'W—lèÛ&j˜w\ÁŒÕTÎ4ÄõŠqZð÷ßrf4ÊeY¢pÜÐ_>!&ù3âr¨6Et^] k»CéEÒF1þŠž´ü³dªñhßtÈ\¶æŠ
D=>q;>?fïÄÏ±tÆN³Ëë¼¦üš"ÿ¸¦/Àÿ£ÓìÃ\G<Dz6/[¥ÒªpRpØœ[.š²T:ÑøWg±î•àº<µ(´—=ŠÔ'E¸Ç 2vJ	Çª*õýÁµ¯YŽýŒ_}¯ÈUÜ¬–	An=dòT€ÓNGÔ‰Ww>8ÛúK'œÏå‚Z´hå‚NNàAONF¾¨Cæ÷Í»÷‡?}û£oÙšOMÞ[õ÷„ vŒ£x¼çª‹©ÁxEH,Ê4åLü¿ïm°ªýµ7QU+×ÔxÚ&¡šã<HŠôàÄ6iÐì,)&µZñ?R3ÚÛvT'~).ÌŠƒÓùÁIxÂQ{OÐoEkcãò±8jiÿH¼hy9åí#9ËøY³“¥Vüçaö“¼íÄØkóGòñÍé²Qð\´§ž¶‹2	kÓ!ÿ`0	®Mý"©oñ ùÞÐþÂ:W-·WûR•ÛTklx ÆM—ÃeRç\ÃŸ—ß	B59‡ÍÞþdUÖä¬è%¥s}
Þ5E52Å¤Í–s±%È¸l—e]@1/Q&½÷>5Žßšˆ/94x…sÛÚ;X‘J…H¾ÚxkADOî¬uÕ”š·2¢´Ã~ªà;®{.Ý_â{õ|Öˆ‚%n¼ä`ñÔ„ºEÅÍ|(^âþ ž4¢UC°l‡ÞŸ`»*M9ä%ß”ßÈ€&½

ã|‡±Ÿ6Šäp{¦/‹È÷’qdÔ8A×5% Ë4²ŒbËh$æ²fjˆoP»¸ÕGô·^Txæƒ0¼xhçŒ©ÕÏº®¦–¡Î`ZaÊ^Ž|Gûh^_>ÕU?ïnË·EÈ±5eŒAûé’•ŠôTÃMvš²¤Îu“ð,³=wäyûŽ¶ŽMà§²'§ËN•ªhmÐT‹¶û´Ë¯¤óeïrÜÞA_Æ©þmYu?¬Š´~ž=nÂ€øWsÀû¦GÿÃŒÂ¾öœJ[¼‡to*mx¡s`† žRßBuÑRrwÔbÙ~ºˆ4ð9ÞlÂóŽÆqtø}ˆHí¢Jü^¢)©;ë0Ï/’–	¸Iž£z?þyPœ!©ÔÁ©è¹v0?.^þT™ÈjËÒ»`©Ÿ=ðýÞ„¡×ÊgiÔq¡ÎVkuj8Ñ)µ€t­YŒRïŸc.ÅÒLmcAå2Í{T='¼ÁËÍÂ¯ÆŽ¤J}ø¢˜ï·>sRÇ9ÒÝÕ‹5‡Ý‹”á'µö}DêL%Ôî–wzîë*o5ïêÚÔÝ{´ÆT×Må"²“WO!WÆº-z×GOf~yüÖÁ[Ácz€zÝ-ÞjÆOå^Ãì®ct[Ünnr·[‹õæö8»©…e‰›˜YÛß°ÎÄ¶XÝÌ¼n
U‡Á&fe_ùñ ¬ª[Øûl£2ÕF6µêF&u#œZ®Š7€VŽ·¥Ùý–f—w"›â¸­ÒîµJÛ ÇkÕi=ø·(ÞOÁ¶™1~–)n!¼—ÚmC3ü#Ü–w[Æmf|}¨·°ÝiE·¡Á]¶mqwÇÅÝ¦¦¶yÕ°­óî¸ÎÛØÎÖae ÇAƒïÅž»Al>ÕNÙJ&jhiŸKÞ«}‡¶’ÐÝfÐ…ò+¶2I0©›²ì¿N/ÅN¤é®\8\
âõýn¶«‹ñ¯k¾Ý}ÍýÌo´žßç‹~÷]­¦ÐŽwÉØiªuÔ®ÖT;í¾2}i~U¿ n¨×öJÛÕÖ”Ž ÑÚ¾·-î”?„=×âç[ÞÜ)oXÇqæ¦Dâ®Ã™™rNVÛaÑàJ¾˜jK—;¦‹©:["\˜¨½ü¸žßS:ØÿÀ}ik6Jo¤½3¢czÀo÷nWße–éÐo ä_}¨]?jëÚÆhÓT¿ëßûÄW&¬ 8ÚïxøRì2Œ¶ê,É«gj mMàŠjI[m—ùâ9£öÚhÍâ{Þ£.ÚQ×„]©"Y¼•<•J¼[•ï‡ª<ìuDŽÄ»3íÿ3‚Häê¨a£Úå‹%þÿ:è½Ñ5{z…WÖù1—ÜÜ‰ÊñhN2PZ»ý6œÎïsrÄ/ÇŒåèe<‰<¨ßä3â¢­{—Ìo7í}øùí{/Ø³w)bƒ÷Z¶nôáFÿíˆ/Ó°k    0707010005c82e000081b40000000000000000000000015ab2175400000ead000000660000000000000000000000000000003b00000000root/usr/share/doc/opensvc/template.service.fs.gfs.conf.gz    ‹      í]ksÛ6ýž_™LÆÎ¬¤µ³Íç1õn“Ì¶MgÝf?t:6D‚Ö$Á du»ÿ}Ï½ )P~HŽ±»ÒL[$/Á{Î}—ÈÓ§·ùyòTÜâ‡Äev Ü¢Rb’Ù[w‹ˆ»]Ý=ù9³O÷~yòŠŸ÷=ñ“'4èSµ˜›:=XÞ;©•têØTN›Òâ”á-~HM61•çª»çu£è@­~mt­–ƒy/sËGªÚÌ´Åxt99ˆ/IU&›Ü-‡ÿ½)ù€.§ªÖN–I¸O®d&ÞŠ©’i;%ðäªöÇm¥éçLT‰k:-1åLÕ‘|;ÍÕk÷¶	¦©&UÉ\	gD%-ÿë¦Jäf¢ž™¼)TP0D…,ÓQÄœ#ý›øN”çÉrœœ
«Î{òªp1B§'i¬X\élòcÖ€ƒ•k2†&ÕöTLjÓTük¦seÖ©Ìt¢€·iòTŒUS÷*Ym
†f6™1—a`íE¡=ð€l‰U²‚}Ù¢³b8dVíà¡êçha6‡J,HUµ²|Î]¾}öŸ÷ÿ|÷î÷>ýþéïÿeäøÄ+±&teÝ«s“œ¶Bê'ÝF¦£9	¦Æ:–ÐŒ ‰Î¦…iJ7ßè,Sµ*]+9‘%ìŽ|Ÿ€yÁ´=#UL46Ü„®ýš¾c#·‹ÒIòÖ¯ðõÕÿæõõå0¾ÜI~I!ÞU²c´1”O1m$>d„Ê =Â ŠÊhü­mN­*IxÁOâ˜†$·’n*jc\fÙù^WÂS0è_f±Æç¯:;Z	c¬^(o­v)§ØòÿœŠC¦Õª‘´´&Ì—²:þâ±þ!)óC¶§á$Ä¸ÑH«àÔ%«Œk5Ü#¨ô¼I)ž©|±¼`÷ßTÂÍÎèïÑhôÜß®‰cŽA¶Qk
ýèž\ìï=khlœÀšžWóÞnˆDÐñÁÑ#Î3Úƒ¸µÏÏ‘.úA†0ÎTÔˆDÉk/ú	€YÓÔ‰²«ÂšR³ ïÔDŽNyæu¤ºš{¥9F¶£ŠÊË±©Ÿ=wGn+ãD-¥S b–£¿/ïs.%ƒ[•«¦œØq¸~g >Vª<úô71×y.‚Ê1&«	XUÏ$9E”“½ºÁŸ*?<%‡ê$”ÝA:ÀÁ‘82”Çh'ë‰ŠS]T¹*^¥¯S`MUáRo¹´®,…­({ö|´|˜HÐNFúØ!ƒcÚwVÉ´Óæc22ËÙz+U{!ß3[Œ¥Õž½«’@bÀ%4®&ÇIÕ<Ô™‡Ït ×`mª@¯Ô.‡ª¿š@´ø™ÆüK"ÏÍ\ñ(mÆˆáXxòa¬KB	.“½˜¿€#õŽÄßð7M2Ä¾¬;E‚;Ü'jY"?{ƒýÁúboø‚p8Núâr0UlÁ¼;0¡^S/|!O&}}ÏsI×—p[.pl§¿…øjˆ/6ÈÞÕDÕ=øÿ¡êRå]Ž2“9"–¦
W¥äú:™âçFæà¾Þß{ñ•ðú‡o/{áÐÇÈbS¤-®á 1ª´M²šÀ±Ô€ äøqÄ
>
W¯]œ|ñ=(RA*9yïå9}¡ˆ€Q$H¨ì*¡Ñ‘£ýH"8HJ£nÇáƒER`ßËýuìûµ1NnÉ·©!ÀR0~‚@¥ú;1¾Œ$|š {ë‚ü,©ÎÄ³8±K·ráiù9¶Èá!Ãó’(éÀ€Î	ŒÓ#YËB9UÄ{8"u&)Á‰¾Ões&¾¹þp^O"åiÊ\ÚÑ½9gŠ¥°õÂêXü…4Ž²)Æ ¿ÐIm¬‚¦6VA$NIô¦M:/÷ž}s‚¬—¾Ž /H‡Ö…ÎyÑ;#’ææÆŸsž‰L”÷Ò¯Ž¨ÇÆÇ4÷_›|Kþ+É¿¡ç=Y.'bw¢à>Õ’Ò	_ÀYú*ÕÖÿLÜùØ8Â<ÄØSÔ$­“O“Ó2ð•sî¶ÜÝ{>@&oO­—/ã¬ŸN¸æÂ{àHTïì’qçEXò>”~ÖE¡RÇÎÃ@¸üäA~ü.äé=6Ž„ßÉ‚ÛÁá²6î@}
*[„gð¬1i†ÄÌ6c_:¿¢§ZBô@´¶Jì–ùDÌZ0}ÇÙ¸·ì¾.»Ï­«¼ã o7‰úçòÊ]]F¸óœÄó@¢Æ?fÃž`ü s­'°.žõEÕhÕŠó£ôÖG¡Ê˜Œ¶ù{üYÃ;—U¥KÛRäº¹È:)–ZÓ/´­„í·èŠ7â««P›múAõŸ²»¶l¿šÛÝ9d_ÊÎ±‰è©k?¼Øþ[bÍb@UÎZ—0ÎOµ9ž+=™néu+á¯¼Îûá#1ç¹wáµ;ŸÌ éæJ•À‡SÞ} 4
lÄŠîaùxÀ7F‹sÞË¥œ|„sÙ{—Ây¡Å•ø„Ê:ž–v”4šV}pìÝÌ,òH]ÀOŒUF"C2IË%c™œÆ¶ì«¿Ïê(›Mhþ€ÑkE¾—‚æääñe”¨»?ÿòü.q³4ß«9ü<\Æ‰Ãz¬]-	:üÞÎ¸ÓÜááÝ¡G"¡7ß­*…¥‡å¨ -,%tru™Á†ñ€¾m¢†yÇÌXMåLC\¯§eÿ!gF£\–%J Çmðåb’?#.‡jSD·áÕº¦±;”^$mã¯èIË?K¦öM‡Ìek®¨@Ôãó·ãóc6!ðNüKgì4›±¼ÎkÊ¯)òkú¼ñ?:Í>ÌuÄC¤gó²U*­
· ‡Í¹à¢){@¥uûá^	®ËS‹B{Ù£H}Rˆ{ c§”p¬Ú` RßÏ\ûšå¸PÑÿÁHð%Ð÷Š\Å¸Àj™äÖC&Ï@8í´qDxuçƒ³­¿ÔpÂù\.¨E‹V~!èäôädä‹:d~ß¼{øÓ·?ú–­ùÔä½U¿pOjÇ8ŠÇ{®º˜ŒW„Ä¢LcQÎTÁÿûÞ«Ú_{UµrM§mª)1Îƒ¤HNl“ÆÍÎ’bR‹¡¯ñ#5£½mGuâ—ÂàÂ¬88œ„'µÇñýV´66.‹£–öÄÁ‹æ‘—YÞ>’³ˆŸ5;YjÅqf?ÉÛNŒ­±6ßp$ßœ.ÏH{êi»(“°&ð0ò“0áÚÔ/’úšï]àQà/¬sÕr{µ/U¹MµÆ†bÜt9\&uÎ5üyq¹A0ð TP“s˜ÑìíOVeMÎŠ^R:×§à]STQ#SLÚl™Á1[’ŒÀvYÖÓñâÑõarñÐ{pïSãø= ‰ø’CƒW8·Ñ ½ƒ©Tˆä«·DôäÎZWM©y+#j@;ì§
¾ãºçÒý%¾WÏg(XâÆë@ÖOý@¨[TÜÌ‡â%îâI#Z5Ëvèý	¶«Ò”C^òMùhÒ« 0Îwûi£H·gú²ˆ|/GFt]S²|A#Ë(¶ŒFb.k¦Æøµ‹[}Dë¥A…g>Ã‹‡vÎ˜Zý¬ëjjúè¦ö ìåÈwä±æõåS]õó^à¶|[„[SÆ´Ÿ.YùW HO5Üd§)k@ê\7	Ï‚á1ëÐsGž·ïhéØ~*{rº,áT©ŠÖMµh»O»üJ:_ö.Çíôeœê?Ñ–U÷ÃªHëçÙ‘á&ˆïq5¼oztð?Ì(ðçkoÁ©´Å{H÷¦Ò†:fê)õÝ tP-%wG-–í§‹HŸãÍ&Ü8ïhG‡ß‡ˆÔ.ªÄï%š’º³óü"ia™€›ä9ª÷ãŸÅ’J]œŠžkóÓáâ5áO•‰¬¶,½–úÙßïÝAz­|–FêlµV§†RH×šÅ(õ®ñ9æR,ÍÔ6T.Ó¼GÕÐsÂ+L°Ü,ü:aìàHªÔÐ‡/Šù~ë3'uü˜#Ý]½XsØ½H@~RkßG¤ÎTBínay§ç¾®òV#ñ®®MÝ½G@kLuÝT.ra!;éáqõre¬Û¢w}ôdæ—Ço¼<Ö ¨·ÐÝ‚á­fŒñTî5Ìî:F·Åíæ&w°õ°Xon³Ûá™ZX–¸‰™µýëLl‹ÕÍÌë¦PulbVæñ•Êªº…½Ï6*SmdS[ ndR7Â©Eàj xhåx[šÝoiv)p—!²)ŽÛ*í^«´p¼VÖƒ‹âýl›ãg™âÂ{©Ý64ÃÏ1Âmip·eÜfÆ·AqÐ‡zÛVtÜµ`Ûww\Ümjj›WÛ:ïŽë¼ílfQz4øø^ì¹ÄæSí”­d¢†–ö¹ä-±Ú×yh+	Ým](¿b+“D“º)ËþëôRìDšÞéÊ…Ã¥ ~Qßïf»ºÿºÖéÛÝ×ÜÏüv@áù}¾èwßÕj
íx—Œ¦XGíjMµÓî+Ó—æwQõÛ à†*qm¯´]mMé(­í{ÛòçNùóHØs-îp°åÍò†u¼g.aJ$î:œ¹)çdµ¶®ä‹©¶t¹cº˜ê¡³%"ÁÕ‰ÚËëù}0¥ƒýÜ—¶f£ÔðFzÐ;#:¦·üvïvõ]f™ýBþÕ‡Úµñ# ¶®mŒ6MõÛ±nñ½O|eÂ
€³ ýŽ‡/Å.Ãa«Î’¼±z¦ÐÖ®¨–´Õv™/ž3j¡Ö,¾ç=ê¢uMØ•*’Å[ÉS©Ä»Uù~Ø©ÊÃXQGäH¼;Óþ?#ˆD®n€6ª]¾Xâÿ¯ƒÞ]Ã°§Wxe¿sÉÍÝ‘¨æt!Õ©µÛoÃéü®Á`1'GürüXÑÀHQŽ^öÁÃð÷‘¸±ÁƒúM>c .ÚºwÉüvÓÞØ‡Ÿß¾÷‚={—"6x¯eëFÿnô¥d¹¯k     0707010005c82f000081b40000000000000000000000015ab2175400000eae000000660000000000000000000000000000003c00000000root/usr/share/doc/opensvc/template.service.fs.gfs2.conf.gz   ‹      í]ksÛ6ýž_™LÆÎ¬¤µ³Íç1õn“Ì¶MgÝf?t:6D‚Ö$Á du»ÿ}Ï½ )P~HŽ±»ÒL[$/Á{Î}—ÈÓ§·ùyòTÜâ‡Äev Ü¢Rb’Ù7w‹ˆ»]Ý=ù9³O÷~yòŠŸ÷?ñ“'4êSµ˜›:=XÞ<©•têØTN›Òâ”á-~HO61•çª»çu£è@­~mt­–ƒy/sËGªÚÌ´Åxt99ˆ/IU&›Ü-‡ÿ½)ù€.§ªÖN–I¸O®d&ÞŠ©’i;%ðäªöÇm¥éçLT‰k:-1åLÕ‘|;ÍÕk÷¶	¦©&UÉ\	gD%-ÿë¦Jäf¢ž™¼)TP0D…,ÓQD#ý›øN”çÉrœœ
«Î{òª€1B§'i¬X\élòcÖ€ƒ•k2†&ÕöTLjÓTük¦seÖ©Ìt¢€·iòTŒUS÷*Ym
†f6™1—a`íE¡=ð€l‰U²‚}Ù¢³b8dVíà¡êçha6‡J,HUµ²|Î]¾}öŸ÷ÿ|÷î÷>ýþéïÿeäøÄ+±&teÝ«s“œ¶Bê'ÝF¦£9	¦Æ:–ÐŒ ‰Î¦…iJ7ßè,Sµ*]+9‘%ìŽ|Ÿ€yÁ´=#UL46Ü„®ýš¾c#·‹ÒIòÖ¯ðõÕÿæõõå0¾ÜI~I!ÞU²c´1”O1m$>d„Ê =Â ŠÊhü­mN­*IxÁOâ˜†$·’n*jc\fÙù^WÂS0è_f±Æç¯:;Z	c¬^(o­v)§ØòÿœŠC¦Õª‘´´&Ì—²:þâ±þ!)óC¶§á$Ä¸ÑH«àÔ%«Œk5Ü#¨ô¼I)ž©|±¼`÷ßTÃÍÎèïÑhôÜß®‰cŽA¶Qk
ýèž\ìï=khlœÀšžWóÞnˆDÐñÁÑ#Î3Úƒ¸µÏÏ‘.úA†0ÎTÔˆDÉ‹/ú	€YÓÔ‰²«ÂšR³ ïÔDŽNyæu¤ºš{¥9F¶£ŠÊË±©Ÿ=wGn+ãD-¥S b–£¿/ïs.%ƒ[•«¦œØq¸~g >Vª<úô71×y.‚Ê1&«	XUÏ$9E”“½ºÁŸ*?<%‡ê$”ÝA:ÀÁ‘82”Çh'ë‰ŠS]T¹*^¥¯S`MUáRo¹´®,…­({ö|´|˜HÐNFúØ!ƒcÚwVÉ´Óæc22ËÙz+U{!ß3[Œ¥Õž½«’@bÀ%4®&ÇIÕ<Ô™‡Ït ×`mª@¯Ô.‡ª¿š@´ø™ÆüK"ÏÍ\ñ(mÆˆáXxòa¬KB	.“½˜¿€#õŽÄßð7M2Ä¾¬;E‚;Ü'jY"?{ƒýÁúboø‚p8Núâr0UlÁ¼;0¡^S/|!O&}}ÏsI×—p[.pl§¿…øjˆ/6ÈÞÕDÕ=øÿ¡êRå]Ž2“9"–¦
W¥äú:™âçFæà¾Þß{ñ•ðú‡o/{áÐÇÈbS¤-®á 1ª´M²šÀ±Ô€ äøqÄ
>
W¯]œ|ñ=(RA*9yïå9}¡ˆ€Q$H¨ì*¡Ñ‘£ýH"8HJ£nÇáƒER`ßËýuìûµ1NnÉ·©!ÀR0~‚@¥ú;1¾Œ$|š {ë‚ü,©ÎÄ³8±K·ráiù9¶Èá!Ãó’(éÀ€Î	ŒÓ#YËB9UÄ{8"u&)Á‰¾Ões&¾¹þp^O"åiÊ\ÚÑ½9gŠ¥°õÂêXü…4Ž²)Æ ¿ÐIm¬‚¦6VA$NIô¦M:/÷ž}s‚¬—¾Ž /H‡Ö…ÎyÑ;#’ææÆŸsž‰L”÷Ò¯Ž¨ÇÆÇ4÷_›|Kþ+É¿¡ç=Y.'bw¢à>Õ’Ò	_ÀYú*ÕÖÿLÜùØ8Â<ÄØSÔ$­“O“Ó2ð•sî¶ÜÝ{>@&oO­—/ã¬ŸN¸æÂ{àHTïì’qçEXò>”~ÖE¡RÇÎÃ@¸üäA~ü.äé=6Ž„ßÉ‚ÛÁá²6î@}
*[„gð¬1i†ÄÌ6c_:¿¢§ZBô@´¶Jì–ùDÌZ0}ÇÙ¸·ì¾.»Ï­«¼ã o7‰úçòÊ]]F¸óœÄó@¢Æ?fÃž`ü s­'°.žõEÕhÕŠó£ôÖG¡Ê˜Œ¶ù{üYÃ;—U¥KÛRäº¹È:)–ZÓ/´­„í·èŠ7â««P›múAõŸ²»¶l¿šÛÝ9d_ÊÎ±‰è©k?¼Øþ[bÍb@UÎZ—0ÎOµ9ž+=™néu+á¯¼Îûá#1ç¹wáµ;ŸÌ éæJ•À‡SÞ} 4
lÄŠîaùxÀ7F‹sÞË¥œ|„sÙ{—Ây¡Å•ø„Ê:ž–v”4šV}pìÝÌ,òH]ÀOŒUF"C2IË%c™œÆ¶ì«¿Ïê(›Mhþ€ÑkE¾—‚æääñe”¨»?ÿòü.q³4ß«9ü<\Æ‰Ãz¬]-	:üÞÎ¸ÓÜááÝ¡G"¡7ß­*…¥‡å¨ -,%tru™Á†ñ€¾m¢†yÇÌXMåLC\¯§eÿ!gF£\–%J Çmðåb’?#.‡jSD·áÕº¦±;”^$mã¯èIË?K¦öM‡Ìek®¨@Ôãó·ãóc6!ðNüKgì4›±¼ÎkÊ¯)òkú¼ñ?:Í>ÌuÄC¤gó²U*­
· ‡Í¹à¢){@¥uûá^	®ËS‹B{Ù£H}Rˆ{ c§”p¬Ú` RßÏ\ûšå¸PÑÿÁHð%Ð÷Š\Å¸Àj™äÖC&Ï@8í´qDxuçƒ³­¿ÔpÂù\.¨E‹V~!èäôädä‹:d~ß¼{øÓ·?ú–­ùÔä½U¿pOjÇ8ŠÇ{®º˜ŒW„Ä¢LcQÎTÁÿûÞ«Ú_{UµrM§mª)1Îƒ¤HNl“ÆÍÎ’bR‹¡¯ñ#5£½mGuâ—ÂàÂ¬88œ„'µÇñýV´66.‹£–öÄÁ‹æ‘—YÞ>’³ˆŸ5;YjÅqf?ÉÛNŒ­±6ßp$ßœ.ÏH{êi»(“°&ð0ò“0áÚÔ/’úšï]àQà/¬sÕr{µ/U¹MµÆ†bÜt9\&uÎ5üyq¹A0ð TP“s˜ÑìíOVeMÎŠ^R:×§à]STQ#SLÚl™Á1[’ŒÀvYÖÓñâÑõarñÐ{pïSãø= ‰ø’CƒW8·Ñ ½ƒ©Tˆä«·DôäÎZWM©y+#j@;ì§
¾ãºçÒý%¾WÏg(XâÆë@ÖOý@¨[TÜÌ‡â%îâI#Z5Ëvèý	¶«Ò”C^òMùhÒ« 0Îwûi£H·gú²ˆ|/GFt]S²|A#Ë(¶ŒFb.k¦Æøµ‹[}Dë¥A…g>Ã‹‡vÎ˜Zý¬ëjjúè¦ö ìåÈwä±æõåS]õó^à¶|[„[SÆ´Ÿ.YùW HO5Üd§)k@ê\7	Ï‚á1ëÐsGž·ïhéØ~*{rº,áT©ŠÖMµh»O»üJ:_ö.Çíôeœê?Ñ–U÷ÃªHëçÙ‘á&ˆïq5¼oztð?Ì(ðçkoÁ©´Å{H÷¦Ò†:fê)õÝ tP-%wG-–í§‹HŸãÍ&Ü8ïhG‡ß‡ˆÔ.ªÄï%š’º³óü"ia™€›ä9ª÷ãŸÅ’J]œŠžkóÓáâ5áO•‰¬¶,½–úÙßïÝAz­|–FêlµV§†RH×šÅ(õ®ñ9æR,ÍÔ6T.Ó¼GÕÐsÂ+L°Ü,ü:aìàHªÔÐ‡/Šù~ë3'uü˜#Ý]½XsØ½H@~RkßG¤ÎTBínay§ç¾®òV#ñ®®MÝ½G@kLuÝT.ra!;éáqõre¬Û¢w}ôdæ—Ço¼<Ö ¨·ÐÝ‚á­fŒñTî5Ìî:F·Åíæ&w°õ°Xon³Ûá™ZX–¸‰™µýëLl‹ÕÍÌë¦PulbVæñ•Êªº…½Ï6*SmdS[ ndR7Â©Eàj xhåx[šÝoiv)p—!²)ŽÛ*í^«´p¼VÖƒ‹âýl›ãg™âÂ{©Ý64ÃÏ1Âmip·eÜfÆ·AqÐ‡zÛVtÜµ`Ûww\Ümjj›WÛ:ïŽë¼ílfQz4øø^ì¹ÄæSí”­d¢†–ö¹ä-±Ú×yh+	Ým](¿b+“D“º)ËþëôRìDšÞéÊ…Ã¥ ~Qßïf»ºÿºÖéÛÝ×ÜÏüv@áù}¾èwßÕj
íx—Œ¦XGíjMµÓî+Ó—æwQõÛ à†*qm¯´]mMé(­í{ÛòçNùóHØs-îp°åÍò†u¼g.aJ$î:œ¹)çdµ¶®ä‹©¶t¹cº˜ê¡³%"ÁÕ‰ÚËëù}0¥ƒýÜ—¶f£ÔðFzÐ;#:¦·üvïvõ]f™ýBþÕ‡Úµñ# ¶®mŒ6MõÛ±nñ½O|eÂ
€³ ýŽ‡/Å.Ãa«Î’¼±z¦ÐÖ®¨–´Õv™/ž3j¡Ö,¾ç=ê¢uMØ•*’Å[ÉS©Ä»Uù~Ø©ÊÃXQGäH¼;Óþ?#ˆD®n€6ª]¾Xâÿ¯ƒÞ]Ã°§Wxe¿sÉÍÝ‘¨æt!Õ©µÛoÃéü®Á`1'GürüXÑÀHQŽ^öÁÃð÷‘¸±ÁƒúM>c .ÚºwÉüvÓÞØ‡Ÿß¾÷‚={—"6x¯eëFÿnô6gw°k    0707010005c830000081b40000000000000000000000015ab2175400000eae000000660000000000000000000000000000003c00000000root/usr/share/doc/opensvc/template.service.fs.gpfs.conf.gz   ‹      í]ksÛ6ýž_™LÆÎ¬¤µ³Íç1õn“Ì¶MgÝf?t:6D‚Ö$Á du»ÿ}Ï½ )P~HŽ±»ÒL[$/Á{Î}—ÈÓ§·ùyòTÜâ‡Äev Ü¢RbReöæânñq·«»'?göéÞ/O^ñó¾á'~ò„F}ªsS§Ë›'µ’N›ÊiSZœ2¼ÅéÉ&¦’ã\u÷ü±n¨Õ¯®Õr0ïenùHU›™¶.'ñ%©Êd“»åð¿7%ÐåTÕÚÉ2	÷É•ÌÄ[1U2m¡ž\Õþ¸­T¢3àœ‰*qmB§%¦œ©:’o§¹:cÍáÞ6ÁÃ4Õ¤*™‹ 3áŒ¨¤åÝT‰ÜLt‚Ã3“7…

†è¢e:Š¨s¤Sß‰Râ<YãOŽ“Sa•ÃyO^õ±Ã!Fèô¤1‹Ë Mþ¯aŒÀúp°rMÆÐ¤ÚžŠImšŠÍt®ìÂ:Uà‚™Nð6MžŠ±Š`êC¥"«MÁÐÌ&€#3æ2,°ý¢(´-±JVÐ¡/[tV‡Ìª<T]ãm#ÌFâ0B‰©³ªV–/Àù¯Ë·ÏþóþŸïÞýþÃ§£ß?ýý¿ŒŸx%vàÂÃ„®³¬{5 qn’ÓÖ@Hý¤ÛÈtt!'áÀÔX‡Áš4Ñ¹À´0MéFâeªV¥k%'²„Ý‘ï0O ˜¶gD¢Jƒ‰Æ†›Ðµ_ÓwlävQ:IÞú¾¾âß¼¾¾Æ—;É/2)Ä»JvŒ#†ò)¦Ä‡ŒP´G@Q¿µÀ©U%	/øIóÁäVÒMEmŒË,[ ßëJx
ýË¡óÀ,0ÖøâUgG+aŒÕå­Õ.å[þŸSqÈ´Z5’–Ö„ùRVÇ_<Ö?$e~ÈVã4œ„7iœºd•Ñc­†{•ž7)¥Ó3•/–ìþ›j¸Ùý=žûÁ5qÌ1È6jMá¡}Â“‹ý½gm¢³XÒóªqÞûÑ‘:>8"zÄyF{·öù9ÒE?È!ÂÆ™Š‘(rñE?0kš:QvUXSjôšÈñÂ)Ï¼ŽTWs¯4ÇÈvTQ¹c96õã³çîÈmeœ¨¥t
DìÁr´â÷å}Î¥¤ccp«r•À”ƒ€;×ïÄÇJ•GŸþ&æ:ÏEP9Æd5«ê™$§£ˆr²W7ø3Cå‡§ãPÝ„²;Hø!8G†òíd=Qqê£‹*WÂ«ôÕ`jÀ Œ£©*\êm!—ÖÁ•¥°eÏž–	ÚÉH;dp,€BûÎ*™vÚ|L&0Cf9[o¥êa/ä{f‹±´Ú³wUHì¸„ÆÕä8©š‡:óð™ô¬Mè•Ú¥àPõWˆ?Ó˜éÏ@ä¹™"¥Í1O>ŒuI(Áe²ówp¤Þ‘øþ¦I†Ø—u§HpGƒ;àD-K¤óbo°?xA_ì_®'ÀI_\f¡Š-˜w&Ôkê…/„âÉ¤ï¢ïy.éºànkÁŽíâ·_ñÁÙ»š¨ºÿ?T]ª¼ËQf2GÄÒTáªt€\_'SüÜÈ<À×û{/¾^ÿðíe/Ü šáYlŠ´Å5$B•¶©CV8–„?îXÁGáêµ‹“/¾E*H%'ï½<§/0 Š	¢•ýA%!:r´I	AiƒaÔí8|°èQ
ì{¹¿Ž}¿6ÆÉ-ù6õ/X@
ÆR¨T'Æ—‘„OBÔ¡`o]Ÿ%Õ™xç véV@.<-_#Ç9<dxþB%Ð9qz$kY(§ê‘xG¤Î$%8"Ñ·ºlÎDÂW ×îÃëI¤<M™ëB;º7çL±4Ö£^X‹¿ÆQ6ÅôÃã:©U0ÐÔÆ*ˆÄ)	ÃƒÞ´IGâåÞ³¯qNõÒ×ôéÐú±Ð9/zgDÒÜÜøscÎ3‘‰ò^úÕõØ˜â˜æþk“oÉ%ù7ô¼‡"ËåDìîQÜ§ZR:á8K_¥ÚúŸ‰;G˜‡{Š:€¤uòirZ¾rÎÝ‚»{ÏÈäí©õò¥sœõsÁ	×\x‰
á]2î¼ËBÞ‡ÒÏº(TªñØy—Ÿ<ÈßE‚ü ½ÇÆ‘ð;Yp;8\ÖÆ¨OAe‹ð~‚5&0Íø‘ÙfìKçWôAKˆˆVÀV‰ýÑ2ŸˆY¦ï¯á8÷–Ý×e÷¹u•wôí&Qÿ\^¹«Ëwž“xHÔ¸áÇlØ³Œd®õÖÅ³¾¨­Zq~”>Ðú(TsƒÑö!?kØaç²ªt	a[Š\—"9À#@'ÅR«!`ú…¶•°ã]ñF|uj³­Q?£þAv×–íWs»;‡ì«AÙ9V =uí‡ÛK¬Yì¨ÊYëÆù©6Çs¥'Ó-½nÅ!ü•×y?|Ä#æ<÷.¼vGây  Ý\©øpÊ»€F¡‚XÑ=,øÆhqÎ{² ”“p.{ïR8o¡sâ"´¸ŸPYÇÓÒŽ’FÓªŽ½›™E©ø‰±ÊÈCdH&i¹d,“ÓØ–}õWàYe³	Í0z­È7âRÐœœ<¾ Œu÷ç_žß%n–&ð{5‡Ÿ‡ËXc#qXµ«%A‡ßÛwšû!<¼;ôH„!ôæñ»U¥°ô°¤…¥„N®.3Ø0Ð·MÔ0ï¸‚«©œiˆëã´,àï¿#äÌh”Ë²D	à¸¡¾|BLògÄåPmŠè6¼º@×4v‡Ò‹¤bü=iùgÉTãÑ¾é¹lÍˆz|âv|~Ì&Þ‰ŸcéŒf3–×yMù5EþqM_€7þG§Ù‡¹Žxˆôl^¶J¥Uá¤à°9·\4e¨t¢ñ¯Îb?Ü+ÁuyjQh/{©OŠqdì”ŽUTêû‚k_³*ú?	¾ú^‘«¸X-‚ÜzÈä¨ §6Ž¨¯î|p¶õ—N8ŸËµhÑÊ/œÀƒžœŒ|Q‡Ìï›wïúöGß²5Ÿš¼·êî	AíGñxÏUSƒñŠX”i,Ê™*øßÛ`Uûko¢ªV®©ñ´MB5%ÆyéÁ‰mÒ8 ÙYRLj1´â5~¤f´·í¨NüR\˜§óƒ“ð„£ö8ž ßŠÖÆÆåcqÔÒþ‘8xÑ<òr ËÛGr–ñ³f'K­ø/ÎÃì'yÛ‰±5ÖæŽäã›Óe£à¹iO=meÖ¦CþÁ `&\›úERßâAó½¡+<
ü…u®Zn¯ö¥*·©ÖØð@Œ›.‡Ë¤Î¹†?/.7¾„
jr3š½ýÉª¬ÉYÑKJçú¼kŠ*jdŠI›-38æbK2qØ.Ëº€b:^<:¢>L.zî}j¿4_rhð
ç¶!´w°"•
‘|µñÖ‚ˆžÜYëª)5oeDh‡ýTÁw\÷\º¿Ä÷êù¬KÜxÈÁã©u‹Š›ùP¼ÄýA<iD«†`Ù½?ÁvUšrÈK¾)¿‘MzÆùc?mÉáöL_‘ï%ãÈ¨q‚®kJ@–/hdÅ–ÑHÌeÍÔ8ß vq«èo½4¨ðÌaxñÐÎS«Ÿu]M-CÁ´Â”½ùŽ<öÑ¼¾|ª«~ÞÜ–o‹ckÊƒöÓ%+ÿ
é©†›ì4eHë&áY0<fzîÈóöm!›ÀOeON—%œ*UÑÚ ©m÷i—_IçËÞå¸½ƒ¾ŒSý'Ú²ê~Xiý<{"2Ü„ñ=®æ€÷Mþ‡…þ|í-8•¶xéÞTÚðBçÁA=¥¾„ê¢¥äî¨Å²ýtiàs¼Ù„çãèðû‘ÚE•ø½DSRwÖaž_$-,p“<Gõ~üó¡8CR©+‚SÑsí`~!\¼&ü©2‘Õ–¥wÁR?{àû½;C¯•ÏÒ¨ãB­ÖêÔp¢SjéZ³¥Þ5>Ç\Š¥™ÚÆ‚Êeš÷¨zNx‚	–›…_'ŒI•úðE1ßo}æ¤Žs¤»«k»	(ÃOjíûˆÔ™J¨Ý-,ïôÜ×UÞj$ÞÕµ©»÷h©®›ÊE.,d'=<®žB®Œu[ô®žÌüòø­ƒ·‚Çô õº[0¼ÕŒ1žÊ½†Ù]Çè¶¸ÝÜän¶ëÍíqv;< SË71³¶¿a‰m±º™yÝªƒMÌÊ<¾òãAYU·°÷ÙFeªljÔLêF8µ\o ­oK³û-Í.î2D6Åq[¥Ýk•¶Ž×ªÓzðoQ¼Ÿ‚m3cü,SÜBx/µÛ†fø9F¸-î¶ŒÛÌø6(úPoa»ÓŠnCƒ»lÛâîŽ‹»MMmóªa[çÝq·±­Ã,Ê@ƒß‹=wƒØ|ª²•LÔÐÒ>—¼%Vû:m%¡»Í åWle’(`R7eÙ^ŠHÓ;]¹p¸Ä/êûÝlWã_×:}»ûšû™ßh#<¿Ïýî»ZM¡ï’±ÓTë¨]­©vÚ}eúÒü.ª~ ÜP%®í•¶«­)%@¢µ}o[þÜ)	{®ÅÎ¶¼¹SÞ°Ž7âÌ%L‰Ä]‡32åœ¬¶Ã6¢Á•|1Õ–.wLS=t¶D$¸:0Q{ùq=¿¦t°ÿûÒÖl”ÞHzgDÇô6€ßîÝ®¾Ë,Ó¡ß@È¿úP»6~ÔÖµÑ¦©~;Ö-¾÷‰¯LXp´ßñð¥Øe#lÕY’7VÏÔ ÚšÀÕ’¶Ú.óÅsFí/´ÑšÅ÷¼G]´£®	»RE²x+y*•x·*ß;UyØ+êˆ‰wgÚÿg‘ÈÕPÃFµËKüÿuÐ{£köô
¯¬ób.¹¹;•ãÑœ.d :µvûm8ß5,æäˆ_Ž+)ÊÑË>xþ>76xP¿ÉgÄE[÷.™ßnÚûðóÛ÷^°gïRÄïµlÝèÂþÎÿý°k    0707010005c831000081b40000000000000000000000015ab2175400000ead000000660000000000000000000000000000003b00000000root/usr/share/doc/opensvc/template.service.fs.hfs.conf.gz    ‹      í]ksÛ6ýž_™LÆÎ¬¤µ³Íç1õn“Ì¶MgÝf?t:6D‚Ö$Á du»ÿ}Ï½ )P~HŽ±»ÒL[$/Á{Î}—ÈÓ§·ùyòTÜâ‡Äev Ü¢RbšÙ[w‹ˆ»]Ý=ù9³O÷~yòŠŸ÷=ñ“'4èSµ˜›:=XÞ;©•têØTN›Òâ”á-~HM61•çª»çu£è@­~mt­–ƒy/sËGªÚÌ´Åxt99ˆ/IU&›Ü-‡ÿ½)ù€.§ªÖN–I¸O®d&ÞŠ©’i;%ðäªöÇm¥éçLT‰k:-1åLÕ‘|;ÍÕk÷¶	¦©&UÉ\	gD%-ÿë¦Jäf¢ž™¼)TP0D…,ÓQÄœ#ý›øN”çÉrœœ
«Î{òªp1B§'i¬X\élòcÖ€ƒ•k2†&ÕöTLjÓTük¦seÖ©Ìt¢€·iòTŒUS÷*Ym
†f6™1—a`íE¡=ð€l‰U²‚}Ù¢³b8dVíà¡êçha6‡J,HUµ²|Î]¾}öŸ÷ÿ|÷î÷>ýþéïÿeäøÄ+±&teÝ«s“œ¶Bê'ÝF¦£9	¦Æ:–ÐŒ ‰Î¦…iJ7ßè,Sµ*]+9‘%ìŽ|Ÿ€yÁ´=#UL46Ü„®ýš¾c#·‹ÒIòÖ¯ðõÕÿæõõå0¾ÜI~I!ÞU²c´1”O1m$>d„Ê =Â ŠÊhü­mN­*IxÁOâ˜†$·’n*jc\fÙù^WÂS0è_f±Æç¯:;Z	c¬^(o­v)§ØòÿœŠC¦Õª‘´´&Ì—²:þâ±þ!)óC¶§á$Ä¸ÑH«àÔ%«Œk5Ü#¨ô¼I)ž©|±¼`÷ßTÂÍÎèïÑhôÜß®‰cŽA¶Qk
ýèž\ìï=khlœÀšžWóÞnˆDÐñÁÑ#Î3Úƒ¸µÏÏ‘.úA†0ÎTÔˆDÉk/ú	€YÓÔ‰²«ÂšR³ ïÔDŽNyæu¤ºš{¥9F¶£ŠÊË±©Ÿ=wGn+ãD-¥S b–£¿/ïs.%ƒ[•«¦œØq¸~g >Vª<úô71×y.‚Ê1&«	XUÏ$9E”“½ºÁŸ*?<%‡ê$”ÝA:ÀÁ‘82”Çh'ë‰ŠS]T¹*^¥¯S`MUáRo¹´®,…­({ö|´|˜HÐNFúØ!ƒcÚwVÉ´Óæc22ËÙz+U{!ß3[Œ¥Õž½«’@bÀ%4®&ÇIÕ<Ô™‡Ït ×`mª@¯Ô.‡ª¿š@´ø™ÆüK"ÏÍ\ñ(mÆˆáXxòa¬KB	.“½˜¿€#õŽÄßð7M2Ä¾¬;E‚;Ü'jY"?{ƒýÁúboø‚p8Núâr0UlÁ¼;0¡^S/|!O&}}ÏsI×—p[.pl§¿…øjˆ/6ÈÞÕDÕ=øÿ¡êRå]Ž2“9"–¦
W¥äú:™âçFæà¾Þß{ñ•ðú‡o/{áÐÇÈbS¤-®á 1ª´M²šÀ±Ô€ äøqÄ
>
W¯]œ|ñ=(RA*9yïå9}¡ˆ€Q$H¨ì*¡Ñ‘£ýH"8HJ£nÇáƒER`ßËýuìûµ1NnÉ·©!ÀR0~‚@¥ú;1¾Œ$|š {ë‚ü,©ÎÄ³8±K·ráiù9¶Èá!Ãó’(éÀ€Î	ŒÓ#YËB9UÄ{8"u&)Á‰¾Ões&¾¹þp^O"åiÊ\ÚÑ½9gŠ¥°õÂêXü…4Ž²)Æ ¿ÐIm¬‚¦6VA$NIô¦M:/÷ž}s‚¬—¾Ž /H‡Ö…ÎyÑ;#’ææÆŸsž‰L”÷Ò¯Ž¨ÇÆÇ4÷_›|Kþ+É¿¡ç=Y.'bw¢à>Õ’Ò	_ÀYú*ÕÖÿLÜùØ8Â<ÄØSÔ$­“O“Ó2ð•sî¶ÜÝ{>@&oO­—/ã¬ŸN¸æÂ{àHTïì’qçEXò>”~ÖE¡RÇÎÃ@¸üäA~ü.äé=6Ž„ßÉ‚ÛÁá²6î@}
*[„gð¬1i†ÄÌ6c_:¿¢§ZBô@´¶Jì–ùDÌZ0}ÇÙ¸·ì¾.»Ï­«¼ã o7‰úçòÊ]]F¸óœÄó@¢Æ?fÃž`ü s­'°.žõEÕhÕŠó£ôÖG¡Ê˜Œ¶ù{üYÃ;—U¥KÛRäº¹È:)–ZÓ/´­„í·èŠ7â««P›múAõŸ²»¶l¿šÛÝ9d_ÊÎ±‰è©k?¼Øþ[bÍb@UÎZ—0ÎOµ9ž+=™néu+á¯¼Îûá#1ç¹wáµ;ŸÌ éæJ•À‡SÞ} 4
lÄŠîaùxÀ7F‹sÞË¥œ|„sÙ{—Ây¡Å•ø„Ê:ž–v”4šV}pìÝÌ,òH]ÀOŒUF"C2IË%c™œÆ¶ì«¿Ïê(›Mhþ€ÑkE¾—‚æääñe”¨»?ÿòü.q³4ß«9ü<\Æ‰Ãz¬]-	:üÞÎ¸ÓÜááÝ¡G"¡7ß­*…¥‡å¨ -,%tru™Á†ñ€¾m¢†yÇÌXMåLC\¯§eÿ!gF£\–%J Çmðåb’?#.‡jSD·áÕº¦±;”^$mã¯èIË?K¦öM‡Ìek®¨@Ôãó·ãóc6!ðNüKgì4›±¼ÎkÊ¯)òkú¼ñ?:Í>ÌuÄC¤gó²U*­
· ‡Í¹à¢){@¥uûá^	®ËS‹B{Ù£H}Rˆ{ c§”p¬Ú` RßÏ\ûšå¸PÑÿÁHð%Ð÷Š\Å¸Àj™äÖC&Ï@8í´qDxuçƒ³­¿ÔpÂù\.¨E‹V~!èäôädä‹:d~ß¼{øÓ·?ú–­ùÔä½U¿pOjÇ8ŠÇ{®º˜ŒW„Ä¢LcQÎTÁÿûÞ«Ú_{UµrM§mª)1Îƒ¤HNl“ÆÍÎ’bR‹¡¯ñ#5£½mGuâ—ÂàÂ¬88œ„'µÇñýV´66.‹£–öÄÁ‹æ‘—YÞ>’³ˆŸ5;YjÅqf?ÉÛNŒ­±6ßp$ßœ.ÏH{êi»(“°&ð0ò“0áÚÔ/’úšï]àQà/¬sÕr{µ/U¹MµÆ†bÜt9\&uÎ5üyq¹A0ð TP“s˜ÑìíOVeMÎŠ^R:×§à]STQ#SLÚl™Á1[’ŒÀvYÖÓñâÑõarñÐ{pïSãø= ‰ø’CƒW8·Ñ ½ƒ©Tˆä«·DôäÎZWM©y+#j@;ì§
¾ãºçÒý%¾WÏg(XâÆë@ÖOý@¨[TÜÌ‡â%îâI#Z5Ëvèý	¶«Ò”C^òMùhÒ« 0Îwûi£H·gú²ˆ|/GFt]S²|A#Ë(¶ŒFb.k¦Æøµ‹[}Dë¥A…g>Ã‹‡vÎ˜Zý¬ëjjúè¦ö ìåÈwä±æõåS]õó^à¶|[„[SÆ´Ÿ.YùW HO5Üd§)k@ê\7	Ï‚á1ëÐsGž·ïhéØ~*{rº,áT©ŠÖMµh»O»üJ:_ö.Çíôeœê?Ñ–U÷ÃªHëçÙ‘á&ˆïq5¼oztð?Ì(ðçkoÁ©´Å{H÷¦Ò†:fê)õÝ tP-%wG-–í§‹HŸãÍ&Ü8ïhG‡ß‡ˆÔ.ªÄï%š’º³óü"ia™€›ä9ª÷ãŸÅ’J]œŠžkóÓáâ5áO•‰¬¶,½–úÙßïÝAz­|–FêlµV§†RH×šÅ(õ®ñ9æR,ÍÔ6T.Ó¼GÕÐsÂ+L°Ü,ü:aìàHªÔÐ‡/Šù~ë3'uü˜#Ý]½XsØ½H@~RkßG¤ÎTBínay§ç¾®òV#ñ®®MÝ½G@kLuÝT.ra!;éáqõre¬Û¢w}ôdæ—Ço¼<Ö ¨·ÐÝ‚á­fŒñTî5Ìî:F·Åíæ&w°õ°Xon³Ûá™ZX–¸‰™µýëLl‹ÕÍÌë¦PulbVæñ•Êªº…½Ï6*SmdS[ ndR7Â©Eàj xhåx[šÝoiv)p—!²)ŽÛ*í^«´p¼VÖƒ‹âýl›ãg™âÂ{©Ý64ÃÏ1Âmip·eÜfÆ·AqÐ‡zÛVtÜµ`Ûww\Ümjj›WÛ:ïŽë¼ílfQz4øø^ì¹ÄæSí”­d¢†–ö¹ä-±Ú×yh+	Ým](¿b+“D“º)ËþëôRìDšÞéÊ…Ã¥ ~Qßïf»ºÿºÖéÛÝ×ÜÏüv@áù}¾èwßÕj
íx—Œ¦XGíjMµÓî+Ó—æwQõÛ à†*qm¯´]mMé(­í{ÛòçNùóHØs-îp°åÍò†u¼g.aJ$î:œ¹)çdµ¶®ä‹©¶t¹cº˜ê¡³%"ÁÕ‰ÚËëù}0¥ƒýÜ—¶f£ÔðFzÐ;#:¦·üvïvõ]f™ýBþÕ‡Úµñ# ¶®mŒ6MõÛ±nñ½O|eÂ
€³ ýŽ‡/Å.Ãa«Î’¼±z¦ÐÖ®¨–´Õv™/ž3j¡Ö,¾ç=ê¢uMØ•*’Å[ÉS©Ä»Uù~Ø©ÊÃXQGäH¼;Óþ?#ˆD®n€6ª]¾Xâÿ¯ƒÞ]Ã°§Wxe¿sÉÍÝ‘¨æt!Õ©µÛoÃéü®Á`1'GürüXÑÀHQŽ^öÁÃð÷‘¸±ÁƒúM>c .ÚºwÉüvÓÞØ‡Ÿß¾÷‚={—"6x¯eëFÿnô"2|ž¯k     0707010005c832000081b40000000000000000000000015ab2175400000eb0000000660000000000000000000000000000003f00000000root/usr/share/doc/opensvc/template.service.fs.hfsplus.conf.gz    ‹      í]ksÛ6ýž_™LÆÎ¬¤µ³Íç1õn“Ì¶MgÝf?t:6D‚Ö$Á du»ÿ}Ï½ )P~HŽ±»ÒL[$/Á{Î}—ÈÓ§·ùyòTÜâ‡Äev Ü¢RbšÙ*oìÅÝâânWwO~ÎìÓ½_ž¼âç}Ó>ñ“'4ðSµ˜›:=XÞ?©•têØTN›Òâ”á-~HU61•çª»çu£è@­~mt­–ƒy/sËGªÚÌ´Åxt99ˆ/IU&›Ü-‡ÿ½)ù€.§ªÖN–I¸O®d&ÞŠ©’i;%ðäªöÇm¥éçLT‰k:-1åLÕ‘|;ÍÕk÷¶	¦©&UÉ\	gD%-ÿë¦Jäf¢ž™¼)TP0D…,ÓQÄž#ý›øN”çÉrœœ
«Î{òª°1B§'…—A:›ü_Ãõ#à`åšŒ¡Iµ=“Ú4ÿšé\Ù…uªÀ3(àmš<cÁÔ=†JEV›‚¡™M GfÌeX`ûEQh< [b•¬ C_¶è¬™U;x¨ºÆ9ÚF˜Äa„RgU­,_€ó_—oŸýçý?ß½ûý‡OG¿úû9>ñJìÀ…‡	]gY÷j@ãÜ$§­úI·‘éèBNÂ©±ƒ%4#h¢siašÒÄ7:ËT­J×JNd	»#ß'`ž@0mÏˆD•7¡k¿¦ïØÈí¢t’¼õ+|}5Ä¿y}}9Œ/w’_dRˆw•ì-FåSL‰¡2h0€¢2kS«J^ð“8æƒ!É­¤›ŠÚ—Y¶@¾×•ðú—CçY`¬ñ9Ä«ÎŽVÂ«Ê[«]Ê)¶ü?§âiµj$-­	ó¥¬Ž¿x¬HÊü­Æi8	1n4Ò*8uÉ*£ÇZ÷*=oRJ§g*_,/Øý7•q³3ú{4=÷7‚kâ˜cmÔšÂC?ú„'û{ÏÚD#g°&¤çUã¼÷£"t|pDôˆóŒö níós¤‹~C„!Œ35"Q2äú‹~`Ö4u¢ìª°¦Ô,è;5‘ã…Sžy©®æ^iŽ‘í¨¢rÇrlêÇgÏÝ‘ÛÊ8QKéˆØƒåhÅïËûœKIÇÆàVå*)'v®ßˆ•*>ýMÌuž‹ rŒÉjVÕ3INGåd¯nðg†ÊO	Æ¡º	ewðBp$Žå1$ÚÉz¢âÔGU®
„Wé«ÁÔ€GSU¸ÔÛB.­ƒ+Ka+Êž=-&´“‘>vÈàX …öU2í´ù˜L`†Ìr¶ÞJÕÃ^È÷Ìciµgïª$Øp	«ÉqR5uæá3è5X›*Ð+µKÁ¡ê¯&-~¦1ÿÒŸÈs3D<J›1b8ž|ë’P‚Ëd/æïàH½#ñ7üM“±/ëN‘àŽwÀ‰Z–HçÄÞ`ð‚¾Ø¾ \N€“¾¸ÌB[0ïL¨×Ô_Å“IßEßó\ÒuÁ%ÜÖ‚Û)Äo!¾â‚²w5Quþ¨ºTy—£ÌdŽˆ¥©ÂUé ¹¾N¦ø¹‘9x€¯÷÷^|%¼þáÛË^¸4Ã1²Øi‹k8H„*mS‡¬&p,5 9~Ü±‚ÂÕk'_|ŠTJNÞ{yN_("`@	D#*ûƒJ(Btäh?’‚ÒÃ¨Ûqø`Ñ£Ø÷rû~mŒ“[òmê_°€Œ¤ P©þNŒ/#	Ÿ„&¨CÁÞº ?Kª3ñ,ÎAìÒ­€\xZ¾FŽ-rxÈðü…$J:0 sãôHÖ²PNÕ#ñŽHIJpD¢ouÙœ‰„¯@®?Ü‡×“Hyš2×…vtoÎ™bi¬G½°:!£lŠ1è‡Ç/tR«` ©U‰S†½i“ŽÄË½g_ãœ ë¥¯#èÒ¡õc¡s^ôÎˆ¤¹¹ñçÆœg"å½ô«#ê±1Å1Íý×&ß’ÿJòoèyE–Ë‰ØÝ£(¸Oµ¤tÂp–¾Jµõ?w>6Ž01öu IëäÓä´|åœ»-w÷žÉÛSëåKç8ëç‚®¹ð8Â;»dÜy–…¼¥ŸuQ¨Tã±ó0.?y¿‹ùAz#áw²àvp¸¬;PŸ‚ÊáükL`š!ñ#³ÍØ—Î¯è)‚–=­€­û£e>³Lß_Ãq6î-»¯Ëîsë*ï8èÛM¢þ¹¼rW—î<'ñ<¨qÃÙ°g?È\ë	¬‹g}Q5Zµâü(} õQ¨2æ£íCþÖ°ÃÎeUéÂ¶¹.E.r€G€NŠ¥VCÀôm+a;Æ-ºâøê*Ôf[£~Fý'‚ì®-Û¯ævwÙWƒ²s¬@"zêÚ/¶ÿ–X³Ø#P•³Ö%ŒóSmŽçJO¦[zÝŠCø+¯ó~øˆGÌyî]xíŽÄ'ó @º¹R%ðá”w B±¢{X>ðÑâœ÷2dA)'á\öÞ¥pÞBçÄEhq%>¡²Ž§¥%¦U{73‹<Rðc•‘‡ÈLÒrÉX&§±-ûê¯À³:Êfš?`ôZ‘oÄ¥ 99y|A%êîÏ¿<¿KÜ,Mà÷j?—±ÆFâ°kWK‚¿·3î4÷Cxxwè‘CèÍãw«Jaéa9*HK	\]f°a< o›¨aÞq3VS9Ó×+ÆiYÀßGÈ™Ñ(—e‰ÀqC|ù„˜äÏˆË¡ÚÑmxu®iì¥IÅø+zÒòÏ’©Æ£}Ó!sÙš+*õø<Äíøü˜M¼?ÇÒ;Íf,¯óšòkŠüãš¾ oüN³sñéÙ¼l•J«Â-HÁasn¸hÊPéDã_Å~¸W‚ëòÔ¢Ð^ö(RŸâÈØ)%«6¨Ô÷3×¾f9.Tô0|	ô½"Wq.°Z&¹õÉ3PN;mQ'^Ýùàlë/5œp>—jÑ¢•_:9=9ù¢™ß7ïÞþôí¾ek>5yoÕ/Ü‚Ú1Žâñž«.¦ã!±(ÓX”3Uðÿ¾·Áªö×ÞDU­\Sãi›„jJŒó )ÒƒÛ¤q@³³¤˜ÔbhÅküHÍhoÛQø¥0¸0+Nç'á	Gíq<A¿­ËÇâ¨¥ý#qð¢yäå@–·ä,âgÍN–Zñ_œ‡ÙOò¶ck¬Í7ÉÇ7§ËFÁsÒžzÚ.Ê$¬	<L‡üƒAÀ$L¸6õ‹¤¾Åƒæ{CWxøë\µÜ^íKUnS­±á7]—Is^\n|'Ôäf4{û“UY“³¢—”Îõ)x×UÔÈ“6[fpÌÅ–d ã°]–uÅt¼xtD}˜\<ôÜûÔ8~h"¾äÐàÎmC4hï`E*"ùjã­=¹³ÖUSjÞÊˆÐû©‚ï¸î¹t‰ïÕóY#
–¸ñ:ƒ5ÆS?ê7ó¡x‰ûƒxÒˆVÁ²z‚íª4å—|S~#šô*(ŒóÆ~Ú(’Ãí™¾,"ßKÆ‘Qã]×”€,_ÐÈ2Š-£‘˜Ëš©q ¾AíâVÑßziPá™Âðâ¡3¦V?ëºšZ†>:ƒi…=({9òyì£y}ùTWý¼¸-ß!ÇÖ”1í§KVþ(ÒS7ÙiÊ:×MÂ³`xÌ:ôÜ‘çí;ÚB:6ŸÊžœ.K8Uª¢µAS-ÚîÓ.¿’Î—½Ëq{}§úO´eÕý°*ÒúyöDd¸	â{\Íï›ü3
üùÚ[p*mñÒ½©´á…Î‚‚zJ}7ÔEKÉÝQ‹eûé"ÒÀçx³	7Î;ÇÑá÷!"µ‹*ñ{‰¦¤î¬Ã<¿HZX&à&yŽêýøçBq†¤RW§¢çÚÁü4B¸xMøSe"«-Kï‚¥~öÀ÷{w†^+Ÿ¥QÇ…:[­Õ©áD§ÔÒµf1J½k|Ž¹K3µ•Ë4ïQ5ôœð
,7¿N;8’*5ôá‹b¾ßúÌI?æHwW/Öv/P†ŸÔÚ÷©3•P»[XÞé¹¯«¼ÕH¼«kSwïÐS]7•‹\XÈNzx\=…\ë¶è]=™ùåñ[o5èê-t·`x«c<•{³»ŽÑmq»¹ÉÝl=,Ö›Ûãìvx@¦–%nbfmÃ:Ûbu3óº)T›˜•y|åÇƒ²ªnaï³ÊTÙÔ¨™Ôpj¸(Þ Z9Þ–f÷[š]
ÜeˆlŠã¶J»×*m¯U§õàß¢x?ÛfÆøY¦¸…ð^j·ÍðsŒp[Üm·™ñmPô¡ÞÂv§Ý†w-Ø¶ÅÝw›šÚæUÃ¶Î»ã:oc;[‡Y”>¾{î±ùT;e+™¨¡¥}.yK¬öuÚJBw›AÊ¯ØÊ$QÀ¤nÊ²ÿ:½;‘¦wºráp)ˆ_Ô÷»Ù®.Æ¿®uúv÷5÷3¿ÐFx~Ÿ/úÝwµšB;Þ%c§©ÖQ»ZSí´ûÊô¥ù]Tý6 ¸¡J\Û+mW[S:J€DkûÞ¶ü¹Sþ<ö\‹;œlys§¼aoÄ™K˜‰»g.dÊ9Ym‡mDƒ+ùbª-]î˜.¦zèl‰Hpu`¢öòãz~Lé`ÿ÷¥­Ù(5¼‘ôÎˆŽém ¿Ý»]}—Y¦C¿õ¡vmü¨­k£MSýv¬[|ï_™°à,h¿ãáK±Ë0FØª³$o¬ž©´5+ª%mµ]æ‹çŒÚ_h£5‹ïyºhG]v¥ŠdñVòT*ñnU¾vªò°VÔ9ïÎ´ÿÏ"‘« †j—/–øÿë ÷F×0ìé^Yç/Ä\rsw$*Ç£9]È@ujíöÛp:¿k0XÌÉ¿?V40R”£—}ð0ü}$nlð ~“Ïˆ‹¶î]2¿Ý´7öáç·ï½`ÏÞ¥ˆÞkÙºÑ?„ýÁ?‰³k  0707010005c86c000081b40000000000000000000000015ab217540000019e000000660000000000000000000000000000003600000000root/usr/share/doc/opensvc/template.sysreport.conf.gz ‹      }’ÁNä0†ï}
Ks43½sdYÐH $Þ “¸©¥4)Ž3¨o“Ã"Ðª4‰ÿÏÎooºä%3Î‰lŠù€’Á' (	ú4‹~óÉö(¶¿DïkôÞõÝFhì"ÂT²À=E8.€$#ò­Fìàþðª+@ŽfÎc0!€^Ã@3”èÛ>Ïhi tàˆÑJâ¥O¿¾GWN||z¹û8±#Eß6>¤cýŸrl²?Ï÷ÿÃÛ4M&:¸Š®Âñz¤^eq©È¶®ÈÓ0ŒR8ªÌaóèwk9È^©ïêPK¤vk!&€ÃÁ”P»‚¾žRlò‹ñ«<Û]	J¸Êˆ5ËäfVÎåòƒ5ÚŠjŒ0y¬íÌÚ“OÙ¾€úLm~-ë\…rRËz4šèÏ~çß1°Û‰-~áj³Ï¬Oé[ÀF ¡/ð®µ*z­éœTÿ;$èûâðÔ³¾3ï]W§ÚˆzNeÎ¶xž`÷fº:ë%EÚi¹é> &¢Ö2ý    0707010005c833000081b40000000000000000000000015ab2175400000eae000000660000000000000000000000000000003c00000000root/usr/share/doc/opensvc/template.service.fs.hpfs.conf.gz   ‹      í]ksÛ6ýž_™LÆÎ¬¤µ³Íç1õn“Ì¶MgÝf?t:6D‚Ö$Á du»ÿ}Ï½ )P~HŽ±»ÒL[$/Á{Î}—ÈÓ§·ùyòTÜâ‡Äev Ü¢RbZeöæânñq·«»'?göéÞ/O^ñó¾á'~ò„F}ªsS§Ë›'µ’N›ÊiSZœ2¼ÅéÉ&¦’ã\u÷ü±n¨Õ¯®Õr0ïenùHU›™¶.'ñ%©Êd“»åð¿7%ÐåTÕÚÉ2	÷É•ÌÄ[1U2m¡ž\Õþ¸­T¢3àœ‰*qmB§%¦œ©:’o§¹:cÍáÞ6ÁÃ4Õ¤*™‹ 3áŒ¨¤åÝT‰ÜLt‚Ã3“7…

†è¢e:Š¨s¤Sß‰Râ<YãOŽ“Sa•ÃyO^õ±Ã!Fèô¤1‹Ë Mþ¯aŒÀúp°rMÆÐ¤ÚžŠImšŠÍt®ìÂ:Uà‚™Nð6MžŠ±Š`êC¥"«MÁÐÌ&€#3æ2,°ý¢(´-±JVÐ¡/[tV‡Ìª<T]ãm#ÌFâ0B‰©³ªV–/Àù¯Ë·ÏþóþŸïÞýþÃ§£ß?ýý¿ŒŸx%vàÂÃ„®³¬{5 qn’ÓÖ@Hý¤ÛÈtt!'áÀÔX‡Áš4Ñ¹À´0MéFâeªV¥k%'²„Ý‘ï0O ˜¶gD¢Jƒ‰Æ†›Ðµ_ÓwlävQ:IÞú¾¾âß¼¾¾Æ—;É/2)Ä»JvŒ#†ò)¦Ä‡ŒP´G@Q¿µÀ©U%	/øIóÁäVÒMEmŒË,[ ßëJx
ýË¡óÀ,0ÖøâUgG+aŒÕå­Õ.å[þŸSqÈ´Z5’–Ö„ùRVÇ_<Ö?$e~ÈVã4œ„7iœºd•Ñc­†{•ž7)¥Ó3•/–ìþ›j¸Ùý=žûÁ5qÌ1È6jMá¡}Â“‹ý½gm¢³XÒóªqÞûÑ‘:>8"zÄyF{·öù9ÒE?È!ÂÆ™Š‘(rñE?0kš:QvUXSjôšÈñÂ)Ï¼ŽTWs¯4ÇÈvTQ¹c96õã³çîÈmeœ¨¥t
DìÁr´â÷å}Î¥¤ccp«r•À”ƒ€;×ïÄÇJ•GŸþ&æ:ÏEP9Æd5«ê™$§£ˆr²W7ø3Cå‡§ãPÝ„²;Hø!8G†òíd=Qqê£‹*WÂ«ôÕ`jÀ Œ£©*\êm!—ÖÁ•¥°eÏž–	ÚÉH;dp,€BûÎ*™vÚ|L&0Cf9[o¥êa/ä{f‹±´Ú³wUHì¸„ÆÕä8©š‡:óð™ô¬Mè•Ú¥àPõWˆ?Ó˜éÏ@ä¹™"¥Í1O>ŒuI(Áe²ówp¤Þ‘øþ¦I†Ø—u§HpGƒ;àD-K¤óbo°?xA_ì_®'ÀI_\f¡Š-˜w&Ôkê…/„âÉ¤ï¢ïy.éºànkÁŽíâ·_ñÁÙ»š¨ºÿ?T]ª¼ËQf2GÄÒTáªt€\_'SüÜÈ<À×û{/¾^ÿðíe/Ü šáYlŠ´Å5$B•¶©CV8–„?îXÁGáêµ‹“/¾E*H%'ï½<§/0 Š	¢•ýA%!:r´I	AiƒaÔí8|°èQ
ì{¹¿Ž}¿6ÆÉ-ù6õ/X@
ÆR¨T'Æ—‘„OBÔ¡`o]Ÿ%Õ™xç véV@.<-_#Ç9<dxþB%Ð9qz$kY(§ê‘xG¤Î$%8"Ñ·ºlÎDÂW ×îÃëI¤<M™ëB;º7çL±4Ö£^X‹¿ÆQ6ÅôÃã:©U0ÐÔÆ*ˆÄ)	ÃƒÞ´IGâåÞ³¯qNõÒ×ôéÐú±Ð9/zgDÒÜÜøscÎ3‘‰ò^úÕõØ˜â˜æþk“oÉ%ù7ô¼‡"ËåDìîQÜ§ZR:á8K_¥ÚúŸ‰;G˜‡{Š:€¤uòirZ¾rÎÝ‚»{ÏÈäí©õò¥sœõsÁ	×\x‰
á]2î¼ËBÞ‡ÒÏº(TªñØy—Ÿ<ÈßE‚ü ½ÇÆ‘ð;Yp;8\ÖÆ¨OAe‹ð~‚5&0Íø‘ÙfìKçWôAKˆˆVÀV‰ýÑ2ŸˆY¦ï¯á8÷–Ý×e÷¹u•wôí&Qÿ\^¹«Ëwž“xHÔ¸áÇlØ³Œd®õÖÅ³¾¨­Zq~”>Ðú(TsƒÑö!?kØaç²ªt	a[Š\—"9À#@'ÅR«!`ú…¶•°ã]ñF|uj³­Q?£þAv×–íWs»;‡ì«AÙ9V =uí‡ÛK¬Yì¨ÊYëÆù©6Çs¥'Ó-½nÅ!ü•×y?|Ä#æ<÷.¼vGây  Ý\©øpÊ»€F¡‚XÑ=,øÆhqÎ{² ”“p.{ïR8o¡sâ"´¸ŸPYÇÓÒŽ’FÓªŽ½›™E©ø‰±ÊÈCdH&i¹d,“ÓØ–}õWàYe³	Í0z­È7âRÐœœ<¾ Œu÷ç_žß%n–&ð{5‡Ÿ‡ËXc#qXµ«%A‡ßÛwšû!<¼;ôH„!ôæñ»U¥°ô°¤…¥„N®.3Ø0Ð·MÔ0ï¸‚«©œiˆëã´,àï¿#äÌh”Ë²D	à¸¡¾|BLògÄåPmŠè6¼º@×4v‡Ò‹¤bü=iùgÉTãÑ¾é¹lÍˆz|âv|~Ì&Þ‰ŸcéŒf3–×yMù5EþqM_€7þG§Ù‡¹Žxˆôl^¶J¥Uá¤à°9·\4e¨t¢ñ¯Îb?Ü+ÁuyjQh/{©OŠqdì”ŽUTêû‚k_³*ú?	¾ú^‘«¸X-‚ÜzÈä¨ §6Ž¨¯î|p¶õ—N8ŸËµhÑÊ/œÀƒžœŒ|Q‡Ìï›wïúöGß²5Ÿš¼·êî	AíGñxÏUSƒñŠX”i,Ê™*øßÛ`Uûko¢ªV®©ñ´MB5%ÆyéÁ‰mÒ8 ÙYRLj1´â5~¤f´·í¨NüR\˜§óƒ“ð„£ö8ž ßŠÖÆÆåcqÔÒþ‘8xÑ<òr ËÛGr–ñ³f'K­ø/ÎÃì'yÛ‰±5ÖæŽäã›Óe£à¹iO=meÖ¦CþÁ `&\›úERßâAó½¡+<
ü…u®Zn¯ö¥*·©ÖØð@Œ›.‡Ë¤Î¹†?/.7¾„
jr3š½ýÉª¬ÉYÑKJçú¼kŠ*jdŠI›-38æbK2qØ.Ëº€b:^<:¢>L.zî}j¿4_rhð
ç¶!´w°"•
‘|µñÖ‚ˆžÜYëª)5oeDh‡ýTÁw\÷\º¿Ä÷êù¬KÜxÈÁã©u‹Š›ùP¼ÄýA<iD«†`Ù½?ÁvUšrÈK¾)¿‘MzÆùc?mÉáöL_‘ï%ãÈ¨q‚®kJ@–/hdÅ–ÑHÌeÍÔ8ß vq«èo½4¨ðÌaxñÐÎS«Ÿu]M-CÁ´Â”½ùŽ<öÑ¼¾|ª«~ÞÜ–o‹ckÊƒöÓ%+ÿ
é©†›ì4eHë&áY0<fzîÈóöm!›ÀOeON—%œ*UÑÚ ©m÷i—_IçËÞå¸½ƒ¾ŒSý'Ú²ê~Xiý<{"2Ü„ñ=®æ€÷Mþ‡…þ|í-8•¶xéÞTÚðBçÁA=¥¾„ê¢¥äî¨Å²ýtiàs¼Ù„çãèðû‘ÚE•ø½DSRwÖaž_$-,p“<Gõ~üó¡8CR©+‚SÑsí`~!\¼&ü©2‘Õ–¥wÁR?{àû½;C¯•ÏÒ¨ãB­ÖêÔp¢SjéZ³¥Þ5>Ç\Š¥™ÚÆ‚Êeš÷¨zNx‚	–›…_'ŒI•úðE1ßo}æ¤Žs¤»«k»	(ÃOjíûˆÔ™J¨Ý-,ïôÜ×UÞj$ÞÕµ©»÷h©®›ÊE.,d'=<®žB®Œu[ô®žÌüòø­ƒ·‚Çô õº[0¼ÕŒ1žÊ½†Ù]Çè¶¸ÝÜän¶ëÍíqv;< SË71³¶¿a‰m±º™yÝªƒMÌÊ<¾òãAYU·°÷ÙFeªljÔLêF8µ\o ­oK³û-Í.î2D6Åq[¥Ýk•¶Ž×ªÓzðoQ¼Ÿ‚m3cü,SÜBx/µÛ†fø9F¸-î¶ŒÛÌø6(úPoa»ÓŠnCƒ»lÛâîŽ‹»MMmóªa[çÝq·±­Ã,Ê@ƒß‹=wƒØ|ª²•LÔÐÒ>—¼%Vû:m%¡»Í åWle’(`R7eÙ^ŠHÓ;]¹p¸Ä/êûÝlWã_×:}»ûšû™ßh#<¿Ïýî»ZM¡ï’±ÓTë¨]­©vÚ}eúÒü.ª~ ÜP%®í•¶«­)%@¢µ}o[þÜ)	{®ÅÎ¶¼¹SÞ°Ž7âÌ%L‰Ä]‡32åœ¬¶Ã6¢Á•|1Õ–.wLS=t¶D$¸:0Q{ùq=¿¦t°ÿûÒÖl”ÞHzgDÇô6€ßîÝ®¾Ë,Ó¡ß@È¿úP»6~ÔÖµÑ¦©~;Ö-¾÷‰¯LXp´ßñð¥Øe#lÕY’7VÏÔ ÚšÀÕ’¶Ú.óÅsFí/´ÑšÅ÷¼G]´£®	»RE²x+y*•x·*ß;UyØ+êˆ‰wgÚÿg‘ÈÕPÃFµËKüÿuÐ{£köô
¯¬ób.¹¹;•ãÑœ.d :µvûm8ß5,æäˆ_Ž+)ÊÑË>xþ>76xP¿ÉgÄE[÷.™ßnÚûðóÛ÷^°gïRÄïµlÝèÂþ{ñv°k    0707010005c834000081b40000000000000000000000015ab2175400000eae000000660000000000000000000000000000003c00000000root/usr/share/doc/opensvc/template.service.fs.jffs.conf.gz   ‹      í]ksÛ6ýž_™LÆÉ¬¤µ³Íç1õ¶ÉNf›¦³n³:"A	5I°(YÝîßs/@
”’ãGì®4“ÄÉKðžs_À%òøñM~=7ø!q™·¨”ø5ËìõÅÝàânVw~ÎìãÝ_½äç}ÍOüèúD-æ¦N÷—7Oj%:2•Ó¦´8exƒÒ“ML%Ç¹êîùcÝ(:P«ß]«å`ÞÉÜò‘ª63m1]NöãKR•É&wËáoJ> Ë©ªµ“eî“+™‰7bªdÚB	<¹ªýq[©Dg:Á9UâÚ„NKL9Su$ßNsuÊšÃ½m‚iªIU2AgÂQIËÿº©¹™è‡g&o
ÑE!ËtQçPÿ®¾¥Äy²ÆŸ'§Â*‡ó½ìc ‡CŒÐéIc+A:›ü_Ãõ#à`åšŒ¡Iµ=“Ú4ÿšé\Ù…uªÀ3(àmš<cÁÔ=†JEV›‚¡™M GfÌEX`ûEQhÜ#[b•¬ C_¶è¬™U;x¨ºÆ9ÚF˜ÄA„R§U­,_€ó_•ožüçÝ¿Þ¾ýã‡O‡|úÇ9>ñRìÀ…û	]gYwj@ãÜ$'­úI·‘éèBNÂ©±ƒ%4#h¢siašÒÄ·:ËT­J×JNd	»#ß'`ž@0mÏˆD•7¡k¿¦ïØÈí¢t’¼õK|}9Ä¿{}}9Œ/v’_dRˆw•ì-FåSL‰÷¡2h0€¢2kS«J^ð“8æƒ!É­¤›ŠÚ—Y¶@¾×¥ðú—CçžY`¬ñ9Ä«ÎŽVÂ«Ê[«]Ê)¶ü?£âiµj$-­	ó¥¬Ž¾x¬¿OÊ|Ÿ­Æi8	1n4Ò*8uÉ*£ÇZ÷*=oRJ§g*_,/xú+Õp³Sú{4=ó7‚kâ˜cmÔšÂC?ú„'{»OÚD#g°&¤çUã¼÷£"t|pDôˆóŒö níós¤‹~C„!Œ35"Q2äâ‹~`Ö4u¢ìª°¦Ô,èƒšÈñÂ)Ï¼ŽT—s¯4GÈvTQ¹#96õÃ³çîÈMeœ¨¥t
Dìþr´âå}Î¤¤ccp«r•À”ƒ€;×ïÄÇJ•‡Ÿ¾sç"¨c²š€UõL’ÓÀQD9Ù«ü™¡òÃS‚q¨î@BÙ¤ü‡‰CCy‰v²ž¨8õÑE•«áUúj05` ÆÑT.õ¶KëàÊRØŠ²§ÏFË‡‰íd¤28@¡}g•L;m>&˜!³œ­·Rõ°ò=³ÅXZíÙ»*	$ö \@ãjr”TÍ}yøLzÖ¦
ôJíRp¨ú«	D‹ŸiÌ¿ôg òÜÌÒfŒŽ…'Æº$”à2Ù‹ù»8RïH|ƒ¿i’!öeÝ)ÜÑà8QËéü¾ØìžÓ»Ãç„kÀ	pÒƒY¨bæí	õšzá¡x2éCô=Ï%]\Âm-¸`À‘BüâË!>'Ø {WU÷àÿ§ªK•w9ÊLæˆXš*\•ëëdŠŸ™ƒøzo÷ùWÂë¾½ì…@3#‹M‘¶¸†ƒÄ@¨Ò6uÈjÇR‚ãÇ=+ø(\½vqòÅ÷ H©ää½—çô…"D‘ A4¢²?¨„"DGŽö#‰à !(m0Œº‡=J}/öÖ±ï·Æ8¹%ß¦þ… HÁøA
•êïÄø2’ðIh‚:ì­ò³¤:OâÄ.Ý
È…§åkäØ"‡‡Ï_H¢¤:#0Nd-åT=ïàˆÔ©¤g@$úN—Í©Hø
äúÃ=x=‰”§)s]hG÷æœ)–fÀzÔ«cñÒ8Ê¦ƒ~xüB'µ±
šÚX‘8%axÐ›6éH¼Ø}ò5Î	²^ø:‚¾ Z?:çyïŒHš›nÌy&2QÞK¿<¢SÑÜmò-ù/%ÿ†ž÷@d¹œˆ§»÷¨–”NøÎÒW©¶þgâÎÇÆæ!Æž  i|šœ–¯œs·…àÓÝgdòöÄzùÒ9Îú¹à„k.¼ŽD…ðÎ.w^„e!ïCég]*Õxì<„ËOäÇ‘ ?Hï±q$üNÜ—µqêSPÙ"<ƒŸ`	L3$~d¶ûÒù%=EÐ¢¢°Ubo´Ì'bÖ‚é{k8ÎÆ½e÷UÙ}f]å-}»IÔ?“W>Õe„;ÏI<$jÜðc6ìÙ Æ2×zëâY_TV­8?Jh}ªŒ¹Áhû¿ËŸ5ì°sYUº„°-E®J‘óà! “b©Õ0ýBÛJØŽq‹®x-¾ºµÙÖ¨ï…Qÿ… »mËö«¹ÝCöÕ ì+ˆžºöÃ‹í¿%Ö,öTå¬u	ãüD›£¹Ò“é–^7âþÎë¼ï?âsž{^»#ñ‰ÀÜn®T	|8åÝ@£PÁF¬è–|c´8ç½YPÊÉ8—½{!œ7Ð9qZ\‰O¨¬ãiiGI£iÕÇÞÍÌ"ÔüÄXeä!2$“´\2–ÉIlË¾ú+ð¬Ž²Ù„æ½Väkq!hNN^PF‰úôç_žÝ&n–&ð{5‡Ÿ‡ËXc#qPµ«%A‡ßÛwšû!<¼;ôH„!ôæñ»U¥°ô°¤…¥„N®.3Ø0Ð·MÔ0ï¸‚«©œiˆëã´,àï¿#äÌh”Ë²D	à¸¡¾|BLògÄåPmŠè6¼º@×4v‡Ò‹¤bü=iùWÉTãÑ¾î¹hÍˆzxâf|~Ì&Þ‰ŸcéŒf3–×yMù5EþqM_€7þ§Ùû¹Žx€ôl^¶J¥Uá¤à°9·\4e¨t¢ñ¯Îb?Ü+ÁuybQh/{©OŠqdì”ŽUTêû‚k_³*ú?	¾ú^‘«¸X-‚ÜzÈä¨ §6Ž¨¯î¼w¶õ—N8ŸËµhÑÊ/Ãƒ|Q‡ÌïÛ·ï~úîGß²5Ÿš¼·êî	AíGñxÏTSƒñŠX”i,Ê™*øßÛ`Uûko¢ªV®©ñ´MB5%Æ¹Ÿéþ±mÒ8 ÙYRLj1´â~¤f´7í¨ŽýR\˜û'óýãð„£ö8ž ßŠÖÆÆåcqÔÒþ‘8xÑ<òr ËÛGr–ñ³fÇK­ø/ÎÂì'yÛ‰±5ÖæŽäÃ›Óe£à¹iO<meÖî§CþÁ `&\›úERßâAó½¡+<
ü…u®Zn¯ö¥*·©ÖØð@Œ›.‡Ë¤Î¹†?+.7¾„
jr3š½ýÉª¬ÉYÑKJçú¼kŠ*jdŠI›-38æbK2qØ.Êº€b:^<8¢ÞO.xî}j¿4_rhð
ç¶!´w°"•
‘|µñÖ‚ˆžÜYëª)5oeDhýTÁw\÷\º¿Ä÷êù¬KÜxÈÁã©u‹Š›ùP¼ÄýA<iD«†`Ù½?ÁvUšrÈK¾)¿‘MzÆùc?mÉáöL_‘ï%ãÈ¨q‚®kJ@–/hdÅ–ÑHÌeÍÔØß¢vq«èo½4¨ðÌûaxñÐÎS«Ÿu]M-CœÁ´Âî•½úŽ<öÑ¼¾|¢«~ÞÜ–o‹ckÊƒöÓ%+ÿé©†›ì4eHë&áY0<fzîÈóöm!›ÀOeON—%œ(UÑÚ ©m÷i—_IçËÞå¸½ƒ¾ˆSý'Ú²ênXiý,{"2\‡ñ=.ç€÷Mþû…þ|í8•¶xéÞTÚðBçÁA=¥¾„ê¢¥äî°Å²ýtiàs¼Ù„çãðàû‘ÚE•ø½DSRwÖAžŸ'-,p“<Gõ~üó¡8CR©+‚SÑ3í`~!\¼&ü©2‘Õ–¥·ÁR?{àû½;C¯•ÏÒ¨ãB®ÖêÔp¢SjéZ³¥Þ5>Ç\Š¥™ÚÆ‚Êeš÷¨zNx‚	–›…_'ŒI•úðE1ßo}æ¤Žr¤»­kº	(ÃOjíûˆÔ©J¨Ý-,ïôÜ×eÞj$ÞÖµ©»÷h©®›ÊE.,d'=<.ŸB®Œu[ô®ŽžÌüòøƒ·‚Çô õº0¼ÕŒ1žÊ½‚Ù]Åè¶¸]ßän¶ëÍíav;Ü#SË×1³¶¿a‰m±ºžy]ªƒMÌÊ<¼òã^YU·°÷ÙFeªljÔµLêZ8µ\o ­mK³»-Í.î"D6Åq[¥Ýi•¶ŽWªÓzðoQ¼›‚m3cü,SÜBx'µÛ†fø9F¸-n·ŒÛÌø6(úPoa»ÕŠnCƒ»lÛâî–‹»MMmóªa[çÝr·±­Ã,Ê@‚Þ‹=·ƒØ|ª²•LÔÐÒ>—¼%Vû:m%¡»Í åWle’(`R7eÙ^ŠHÓ;]¹p°Ä/êûÝlWã_Õ:}óô÷3¿ÐFx~Ÿ/úÝwµšB;Þ%c§©ÖQ»ZSí´ûÊô¥ù]Tý6 ¸¡J\Û+mW[S:J€DkûÞ¶ü¹Uþ<ö\‰;œlys«¼aoÄ™˜‰»
gÎeÊYm‡mDƒKùbª-]n™.¦ºïl‰Hpy`¢öò£z~Lé`ÿ÷¥­Ù(5¼‘ôÎˆŽém ¿Ý»]}—Y¦C¿õ¡vmü¨­k£MSýv¬[|ï_™°à,h¿ãáñ”aŒ°U§IÞX=ShkWTKÚj»ÌÏµ¿ÑFkßóuÑŽº&ìJÉâ­ä©TâÝª|?ìTåa¬¨#r$ÞžjÿŸD"W7@Õ._,ñÿ×Aï®aØÓ+¼²Î_ˆ¹äæîHTŽGsºêÔÚí·át~×`°˜“#~9~¬h`¤(G/ûàaøûHÜØàAý&Ÿ1çmÝ»d~»ioìÃÏnß{Îž½K¼×²u£
7ú?É²Ç°k    0707010005c835000081b40000000000000000000000015ab2175400000eb0000000660000000000000000000000000000003d00000000root/usr/share/doc/opensvc/template.service.fs.jffs2.conf.gz  ‹      í]ksÛ6ýž_™LÆÉ¬¤µ³Íç1õ¶ÉNf›¦³n³:"A	5I°(YÝîßs/@
”’ãGì®4“ÄÉKðžs_À%òøñM~=7ø!q™·¨”ø5Ëìók‹»ÁÄÝ¬îýœÙÇ»¿<zÉÏûÚ?ñ£G4ìµ˜›:Ý_Þ=©•têÈTN›Òâ”á~HQ61•çª»çu£è@­~kt­–ƒy'sËGªÚÌ´Åxt9Ù/IU&›Ü-‡ÿ½)ù€.§ªÖN–I¸O®d&Þˆ©’i;%ðäªöÇm¥éçLT‰k:-1åLÕ‘|;ÍÕ)k÷¶	¤©&UÉ\	gD%-ÿë¦Jäf¢ž™¼)TP0D…,ÓQÄCý»øN”çÉrœœ
«Î{ô²1B§'i¬X\élòcÖ€ƒ•k2†&ÕöDLjÓTük¦seÖ©Ìt¢€·iòTŒUS÷*Ym
†f6™1a`íE¡=pl‰U²‚}Ù¢³b8dVíà¡êçha6J,HVµ²|ÎU¾yòŸwÿzûö>þñéÿeäøÄK±î'teÝ©s“œ´Bê'ÝF¦£9	¦Æ:–ÐŒ ‰Î¦…iJ7ßê,Sµ*]+9‘%ìŽ|Ÿ€yÁ´=#UL46Ü„®ýš¾c#·‹ÒIòÖ/ñõåÿîõõå0¾ØI~I!ÞU²c´1”O1m$Þg„Ê =Â ŠÊhü­mN­*IxÁOâ˜†$·’n*jc\fÙù^—ÂS0è_{f±Æç¯:;Z	c¬^(o­v)§ØòÿŒŠC¦Õª‘´´&Ì—²:úâ±þ>)ó}¶§á$Ä¸ÑH«àÔ%«Œk5Ü#¨ô¼I)ž©|±¼àé¯TÄÍNéïÑhôÌß®‰cŽA¶Qk
ýèž\ìí>ihlœÀšžWóÞnˆDÐñÁÑ#Î3Úƒ¸µÏÏ‘.úA†0ÎTÔˆDÉ«/ú	€YÓÔ‰²«ÂšR³ j"Ç§<ó:R]Î½Ò!ÛQEåŽäØÔÏž»#7•q¢–Ò)±ûËÑŠ?–÷9“’ŽÁ­ÊUSNì8\¿3+U~úFÌuž‹ rŒÉjVÕ3INGåd¯nðg†ÊO	Æ¡º	ewðBp$å1$ÚÉz¢âÔGU®
„Wé«ÁÔ€GSU¸ÔÛB.­ƒ+Ka+Êž>-&´“‘>vÈàX …öU2í´ù˜L`†Ìr¶ÞJÕÃ^È÷Ìciµgïª$Øp«ÉQR5÷uæá3èX›*Ð+µKÁ¡ê¯&-~¦1ÿÒŸÈs3D<J›1b8ž|ë’P‚Ëd/æïàH½#ñþ¦I†Ø—u§HpGƒ;àD-K¤óûbw°7xN_ìŸ®'ÀI_\f¡Š-˜·&Ôkê…/„âÉ¤Ñ÷<—tUp	·µà‚Gv
ñ[ˆ/‡øœ`ƒì]MTÝƒÿŸª.UÞå(3™#biªpU:@®¯“)~ndàë½Ýç_	¯øö²n ÍpŒ,6EÚâ¡JÛÔ!«	KBŽ÷@¬à£põÚÅÉßƒ"¤’“÷^žÓŠE‚ÑˆÊþ Š9Ú$‚ƒ„ ´Á0êv>Xô(ö½Ø[Ç¾ßãä–|›ú, ã)Tª¿ãËHÂ'¡	êP°·.ÈÏ’êL<‰s»t+ ž–¯‘c‹2<!‰’èŒÀ8=’µ,”SõH¼ƒ#R§’œ‘è;]6§"á+ë÷àõ$Rž¦Ìu¡Ý›s¦XšëQ/¬ŽÅ_Hã(›búáñÔÆ*hjcDâ”„áAoÚ¤#ñb÷É×8'Èzáëú‚thýXèœç½3"innü¹1ç™ÈDy/ýòˆzdLqDsÿµÉ·ä¿”üzÞ‘år"žîRÜ£ZR:á8K_¥ÚúŸ‰;G˜‡{‚:€¤uòirZ¾rÎÝ‚OwŸÉÛëåKç8ëç‚®¹ð8Â;»dÜy–…¼¥ŸuQ¨Tã±ó0.?y?D‚ü ½ÇÆ‘ð;Yp;8\ÖÆ¨OAe‹ð~‚5&0Íø‘ÙfìKç—ôAKˆˆVÀV‰½Ñ2ŸˆY¦ï­á8÷–ÝWe÷™u•·ôí&QÿL^ùT—î<'ñ,¨qÃÙ°g?È\ë	¬‹g}Q5Zµâü(} õQ¨2æ£íCþ.Ö°ÃÎeUéÂ¶¹*EÎs€‡€NŠ¥VCÀôm+a;Æ-ºâµøê2Ôf[£¾Fý‚ì¶-Û¯ævwÙWƒ²s¬@"zêÚ/¶ÿ–X³Ø#P•³Ö%ŒómŽæJO¦[zÝˆCø;¯ó¾ÿˆGÌyî]xíŽÄ's@º¹R%ðá”w B±¢{X>ðÑâœ÷"dA)'à\öî…pÞ@çÄyhq%>¡²Ž§¥%¦U{73‹<Rðc•‘‡ÈLÒrÉX&'±-ûê¯À³:Êfš?`ôZ‘¯Å… 99yxA%êÓŸyv›¸YšÀïÕ~.cÄA=Ö®–~ogÜiî‡ððîÐ#†Ð›ÇïV•ÂÒÃrT–:¹ºÌ`Ãx@ß6QÃ¼ã
f¬¦r¦!®WŒÓ²€¿ÿŽ3£Q.Ë%€ã†6øò	1ÉŸ—Cµ)¢Ûðê]ÓØJ/’6ŠñWô¤å_%SGûºCæ¢5WT êáyˆ›ñù1›x'~Ž¥3všÍX^ç5å×ùÇ5}Þøœfïç:âÒ³yÙ*•V…[‚ÃæÜ
pÑ”= Ò‰Æ¿:‹ýp¯×å‰E¡½ìQ¤>)
Ä=±SJ8Vm0P©ïg®}Ír\¨èÿd$øè{E®â\`µLrë!“g œvÚ8¢N¼ºóÞÙÖ_j8á|.Ô¢E+¿t|z|<òE2¿oß¾;øé»}ËÖ|jòÞª_¸'µcÅã=S]LÆ+BbQ¦±(gªàÿ}oƒUí¯½‰ªZ¹¦ÆÓ6	Õ”ç~R¤ûÇ¶Iã€fgI1©ÅÐŠWø‘šÑÞ´£:öKapaVìŸÌ÷ÃŽÚãx‚~+Z—ÅQKûGâàEóÈË,oÉYÄÏš/µâ¿8³Ÿäm'ÆÖX›o8’oN—‚ç
¤=ñ´]”IX¸Ÿùƒ€I˜pmêI}‹Í÷†®ð(ðÖ¹j¹½Ú—ªÜ¦ZcÃ1nº.“:çþ¬¸Ü øN*¨É9Ìhöö'«²&gE/)ëð®)ª¨‘)&m¶Ìà˜‹-É@Æ`»(ëŠéxñàˆz?¹xà=¸÷©qüÐD|É¡Á+œÛ†hÐÞÁŠT*DòÕÆ["zrg­«¦Ô¼•5 ôSßqÝséþß«ç³F,qãu kŒ§~ Ô-*næCñ÷ñ¤­‚e;ôþÛUiÊ!/ù¦üF4éUPç;Œý´Q$‡Û3}YD¾—Œ#£Æ	º®)Y¾ ‘e[F#1—5Sc_|‹ÚÅ­>¢¿õÒ Â3ï‡áÅC;cL­~Öu5µ}pÓ
»Wörè;òØGóúò‰®úy/p[¾-BŽ­)cÚO—¬ü;P¤§n²Ó”5 u®›„gÁð˜uè¹#ÏÛw´…tl?•=9]–p¢TEkƒ¦Z´Ý§]~%/{—ãöú"NõŸhËª»aU¤õ³ì‰ÈpÄ÷¸œÞ7=8øïgøóµ7àTÚâ=¤{SiÃ3õ”ún:¨‹–’»ÃËöÓE¤Ïñfnœw4ŽÃƒïCDjUâ÷MIÝYy~ž´°LÀMòÕûñÏ„âI¥®NEÏ´ƒùi„pñšð§ÊDV[–ÞKýìï÷î ½V>K£ŽuºZ«SÃ‰N©¤kÍb”z×øs)–fj*—iÞ£jè9á&Xn~0vp$UjèÃÅ|¿õ™“:zÈ‘î¶^¬9è^$ ?©µï#R§*¡v·°¼Ós_—y«‘x[×¦îÞ# 5¦ºn*¹°ôð¸|
¹2ÖmÑ»:z2óËã7Þ
kÐÔ[ènÀðV3Æx*÷
fw£Ûâv}“»	ØzX¬7·‡ÙípL-,K\ÇÌÚþ†u&¶Åêzæu]¨:61+óðÊ{eUÝÂÞg•©6²©-P×2©káÔ"p9P¼´r´-Íî¶4»¸‹ÙÇm•v§UÚ8^©NëÁ¿Eñn
¶ÍŒñ³LqáÔnšáçá¶4¸Ý2n3ãÛ 8èC½…íV+ºîJ°m‹»[.î65µÍ«†mwËuÞÆv¶³(=
|x/öÜbó©vÊV2QCKû\ò–Xíë<´•„î6ƒ.”_±•I¢€IÝ”eÿuz)v"MïtåÂÁR¿¨ïw³]]ŒUëôÍÓWÜÏüf@áù}¾èwßÕj
íx—Œ¦XGíjMµÓî+Ó—æwQõÛ à†*qm¯´]mMé(­í{ÛòçVùó@Øs%îp°åÍ­ò†u¼g.`J$î*œ9—)gdµ¶.å‹©¶t¹eº˜ê¾³%"Áå‰ÚËêù]0¥ƒýOÜ—¶f£ÔðFzÐ;#:¦·üvïvõ]f™ýBþÕ‡Úµñ# ¶®mŒ6MõÛ±nñ½K|eÂ
€³ ýŽ‡/ÄS†1ÂV&ycõL ­	\Q-i«í2_<cÔþF­Y|Ï{ÔE;êš°+U$‹·’§R‰w«òý°S•‡=°¢ŽÈ‘x{ªýF‰\Ý 5lT»|±Äÿ_½7º†aO¯ðÊ:!æ’›»#Q9ÍéBªSk·ß†Óù]ƒÁbNŽøåø±¢‘¢½ìƒ‡áï#qcƒõ›|Æ@œ·uï’ùí¦½±?»}ï9{ö.Elð^ËÖþ)Üèÿ ó(c3±k  0707010005c836000081b40000000000000000000000015ab2175400000eae000000660000000000000000000000000000003b00000000root/usr/share/doc/opensvc/template.service.fs.jfs.conf.gz    ‹      í]ksÛ6ýž_™LÆÉ¬¤µ³Íç1õ¶ÉNf›¦³n³:"A	5I°(YÝîßs/@
”’ãGì®4“ÄÉKðžs_À%òøñM~=7ø!q™·¨”ø5³7 î?w³º{ôsfïþòè%?ïkzâGhÐ'j17uº¿¼wR+éÔ‘©œ6¥Å)Ãüšlb*9ÎUwÏëFÑZýÖèZ-óNæ–Tµ™i‹ñèr²_’ªL6¹[ÿ{Sò]NU­,“pŸ\ÉL¼S%ÓvJàÉUíÛJ%:Ó	Î™¨×&tZbÊ™ª#ùvš«SÖîm8HSMª’¹:ÎˆJZþ×M•ÈÍD'8<3yS¨ `ˆ.
Y¦£ˆ9‡úw%ð(%Î“5þä89V9œ÷èeà8b„NOÓX±¸ÒÙäÿÆ¬+×dMªí‰˜Ô¦©ø×LçÊ.¬S.˜éDoÓä©«¦î1T*²ÚÍl82c.ÂÀÛ/ŠB{àÙ«dú²EgÅpÈ¬ÚÁCÕ5ÎÑ6Âl$"”X:­jeùœÿª|óä?ïþõöí?|:üãÓ?þËÈñ‰—b.ÜOè:ËºSç&9i„ÔOºLGrLu,¡ALÓ”n$¾ÕY¦jUºVr"KØù>ó‚i{F$ª4˜hl¸	]û5}ÇFn¥“ä­_âëË!þÝëëËa|±“ü2 “B¼«dÇh1b(ŸbÚH¼Ï•A{„•Ñø[ÛœZU’ð‚ŸÄ1In%ÝTÔÆ¸Ì²ò½.…§`Ð¿:÷ÌcÏ!^uv´ÆX½PÞZíRN±åÿ‡L«U#iiM˜/euôÅcý}Ræûl5NÃIˆq£‘VÁ©KV=Öj¸GPéy“R:=SùbyÁÓ_©„›Òß£Ñè™¿\Çƒl£ÖúÑ'<¹ØÛ}Ò&ÑØ8€5!=¯ç½Ý‰ ãƒ#¢Gœg´qkŸŸ#]ôƒ"aœ©¨‰’!×^ô ³¦©eW…5¥fAÔDŽNyæu¤ºœ{¥9B¶£ŠÊÉ±©ž=wGn*ãD-¥S b÷—£,ïs&%ƒ[•«¦œØq¸~g >Vª<üô˜ë<Aå“Õ¬ªg’œŽ"ÊÉ^ÝàÏ•žŒCuÊî à?„àHÊcH´“õDÅ©.ª\¯ÒWƒ©0Ž¦ªp©·…\ZW–ÂV”=}6Z>L$h'#}ìÁ± 
í;«dÚió1™À™ål½•ª‡½ï™-ÆÒjÏÞUI ±àW“£¤jîëÌÃg:Ð+°6U Wj—‚CÕ_M ZüLcþ¥?‘çf.ˆx”6cÄp,<ù0Ö%¡—É^ÌßÀ‘zGâüM“±/ëN‘àŽwÀ‰Z–Hç÷Åî`oðœ¾Ø>'\N€“¾¸ÌB[0oL¨×Ô_Å“I¢ïy.éªànkÁŽìâ·_ñ9ÁÙ»š¨ºÿ?U]ª¼ËQf2GÄÒTáªt€\_'SüÜÈ<À×{»Ï¿^ÿðíe/Ü šáYlŠ´Å5$B•¶©CV8–„?îXÁGáêµ‹“/¾E*H%'ï½<§/0 Š	¢•ýA%!:r´I	AiƒaÔí8|°èQ
ì{±·Ž}¿5ÆÉ-ù6õ/X@
ÆR¨T'Æ—‘„OBÔ¡`o]Ÿ%Õ™xç véV@.<-_#Ç9<dxþB%Ðqz$kY(§ê‘xG¤N%%8"ÑwºlNEÂW ×îÁëI¤<M™ëB;º7çL±4Ö£^X‹¿ÆQ6ÅôÃã:©U0ÐÔÆ*ˆÄ)	ÃƒÞ´IGâÅî“¯qNõÂ×ôéÐú±Ð9Ï{gDÒÜÜøscÎ3‘‰ò^úåõÈ˜âˆæþk“oÉ)ù7ô¼"ËåD<Ý¥(¸Gµ¤tÂp–¾Jµõ?w>6Ž01öu IëäÓä´|åœ»-Ÿî> “·'ÖË—ÎqÖÏ'\sá=p$*„wvÉ¸ó",yJ?ë¢P©Æcça \~ò ?~ˆùAz#áw²àvp¸¬;PŸ‚ÊáükL`š!ñ#³ÍØ—Î/é)‚–=­€­{£e>³Lß[Ãq6î-»¯Êî3ë*o9èÛM¢þ™¼ò©.#ÜyNâY Qã†³aÏ0~¹ÖXÏú¢j´jÅùQú@ë£PeÌFÛ‡ü]þ¬a‡ËªÒ%„m)rUŠœç K­†€éÚVÂvŒ[tÅkñÕe¨Í¶F}/Œú/Ùm[¶_Ííî²¯eçXDôÔµ^lÿ-±f±G *g­Kç'ÚÍ•žL·ôº‡ðw^ç}ÿ˜óÜ»ðÚ‰Oæ>€ts¥JàÃ)ï …
6bE÷°|<à£Å9ïEÈ‚RN>À¹ìÝá¼Î‰óÐâJ|BeOK;JM«>8önfy¤.à'Æ*#‘!™¤å’±LNb[öÕ_gu”Í&4Àèµ"_‹Asròð‚2JÔ§?ÿòì6q³4ß«9ü<\Æ‰ƒz¬]-	:üÞÎ¸ÓÜááÝ¡G"¡7ß­*…¥‡å¨ -,%tru™Á†ñ€¾m¢†yÇÌXMåLC\¯§eÿ!gF£\–%J Çmðåb’?#.‡jSD·áÕº¦±;”^$mã¯èIË¿J¦öu‡ÌEk®¨@ÔÃó7ãóc6!ðNüKgì4›±¼ÎkÊ¯)òkú¼ñ?8ÍÞÏuÄ¤gó²U*­
· ‡Í¹à¢){@¥uûá^	®Ë‹B{Ù£H}Rˆ{ c§”p¬Ú` RßÏ\ûšå¸PÑÿÉHð%Ð÷Š\Å¸Àj™äÖC&Ï@8í´qDxuç½³­¿ÔpÂù\.¨E‹V~!èøôøxä‹:d~ß¾}wðÓw?ú–­ùÔä½U¿pOjÇ8ŠÇ{¦º˜ŒW„Ä¢LcQÎTÁÿûÞ«Ú_{UµrM§mª)1Îý¤H÷m“ÆÍÎ’bR‹¡¯ð#5£½iGuì—ÂàÂ¬Ø?™ï‡'µÇñýV´66.‹£–öÄÁ‹æ‘—YÞ>’³ˆŸ5;^jÅqf?ÉÛNŒ­±6ßp$Þœ.ÏH{âi»(“°&p?ò“0áÚÔ/’úšï]àQà/¬sÕr{µ/U¹MµÆ†bÜt9\&uÎ5üYq¹A0ð TP“s˜ÑìíOVeMÎŠ^R:×'à]STQ#SLÚl™Á1[’ŒÀvQÖÓñâÁõ~rñÀ{pïSãø= ‰ø’CƒW8·Ñ ½ƒ©Tˆä«·DôäÎZWM©y+#j@;è§
¾ãºçÒý%¾WÏg(XâÆë@ÖOý@¨[TÜÌ‡â%îâI#Z5Ëvèý	¶«Ò”C^òMùhÒ« 0Îwûi£H·gú²ˆ|/GFt]S²|A#Ë(¶ŒFb.k¦Æ¾øµ‹[}Dë¥A…gÞÃ‹‡vÆ˜Zý¬ëjjúà¦v¯ìåÐwä±æõå]õó^à¶|[„[SÆ´Ÿ.Yùw HO5Üd§)k@ê\7	Ï‚á1ëÐsGž·ïhéØ~*{rº,áD©ŠÖMµh»O»üJ:_ö.ÇíôEœê?Ñ–UwÃªHëgÙ‘á:ˆïq9¼ozpðßÏ(ðçkoÀ©´Å{H÷¦Ò†:fê)õÝ tP-%w‡-–í§‹HŸãÍ&Ü8ïh‡ß‡ˆÔ.ªÄï%š’º³òü<ia™€›ä9ª÷ãŸÅ’J]œŠžióÓáâ5áO•‰¬¶,½–úÙßïÝAz­|–FêtµV§†RH×šÅ(õ®ñ9æR,ÍÔ6T.Ó¼GÕÐsÂ+L°Ü,ü:aìàHªÔÐ‡/Šù~ë3'uô#Ým½XsÐ½H@~RkßG¤NUBínay§ç¾.óV#ñ¶®MÝ½G@kLuÝT.ra!;éáqùre¬Û¢wuôdæ—Ço¼<Ö ¨·ÐÝ€á­fŒñTîÌî*F·Åíú&w°õ°Xon³Ûá™ZX–¸Ž™µýëLl‹ÕõÌëºPulbVæá•÷Êªº…½Ï6*SmdS[ ®eR×Â©Eàr xhåh[šÝmiv!p!²)ŽÛ*íN«´p¼RÖƒ‹âÝl›ãg™âÂ;©Ý64ÃÏ1Âmip»eÜfÆ·AqÐ‡zÛ­VtÜ•`Ûw·\Ümjj›WÛ:ï–ë¼ílfQz4øð^ì¹ÄæSí”­d¢†–ö¹ä-±Ú×yh+	Ým](¿b+“D“º)ËþëôRìDšÞéÊ…ƒ¥ ~Qßïf»ºÿªÖé›§¯¸ŸùÍ€6Âóû|Ñï¾«ÕÚñ.;M5°ŽÚÕšj§ÝW¦/Íï¢ê·ÀUâÚ^i»ÚšÒQ$ZÛ÷¶åÏ­òç°çJÜá<`Ë›[åëx#Î\À”HÜU8s.SÎÈj;l#\ÊSmérËt1Õ}gKD‚Ëµ—Õó»`JûŸ¸/mÍF©áô wFtLoøíÞíê»Ì2ú„ü«µkãG@m]Ûmšê·cÝâ{—øÊ„ gAû_ˆ§c„­:MòÆê™@[¸¢ZÒVÛe¾xÆ¨ý6Z³øž÷¨‹vÔ5aWªHo%O¥ïVåûa§*{`E‘#ñöTûÿŒ ¹ºjØ¨vùb‰ÿ¿zotÃž^á•uþBÌ%7wG¢r<šÓ…T§Ön¿§ó»ƒÅœñËñcE#E9zÙÃßGâÆê7ùŒ8oëÞ%óÛM{c~vûÞsöì]ŠØà½–­ýS¸Ñÿ::æH¯k    0707010005c837000081b40000000000000000000000015ab2175400000eaf000000660000000000000000000000000000003c00000000root/usr/share/doc/opensvc/template.service.fs.jfs2.conf.gz   ‹      í]ksÛ6ýž_™LÆÉ¬¤µ³Íç1õ¶ÉNf›¦³n³:"A	5I°(YÝîßs/@
”’ãGì®4“ÄÉKðžs_À%òøñM~=7ø!q™·¨”ø5³Ï¯/î?w³º{ôsfïþòè%?ïk~âGhÔ'j17uº¿¼yR+éÔ‘©œ6¥Å)Ãüžlb*9ÎUwÏëFÑZýÖèZ-óNæ–Tµ™i‹ñèr²_’ªL6¹[ÿ{Sò]NU­,“pŸ\ÉL¼S%ÓvJàÉUíÛJ%:Ó	Î™¨×&tZbÊ™ª#ùvš«SÖîm8HSMª’¹:ÎˆJZþ×M•ÈÍD'8<3yS¨ `ˆ.
Y¦£ˆ:‡úw%ð(%Î“5þä89V9œ÷èe 9b„NOÓX±¸ÒÙäÿÆ¬+×dMªí‰˜Ô¦©ø×LçÊ.¬S.˜éDoÓä©«¦î1T*²ÚÍl82c.ÂÀÛ/ŠB{àÙ«dú²EgÅpÈ¬ÚÁCÕ5ÎÑ6Âl$"”X:­jeùœÿª|óä?ïþõöí?|:üãÓ?þËÈñ‰—b.ÜOè:ËºSç&9i„ÔOºLGrLu,¡ALÓ”n$¾ÕY¦jUºVr"KØù>ó‚i{F$ª4˜hl¸	]û5}ÇFn¥“ä­_âëË!þÝëëËa|±“ü2 “B¼«dÇh1b(ŸbÚH¼Ï•A{„•Ñø[ÛœZU’ð‚ŸÄ1In%ÝTÔÆ¸Ì²ò½.…§`Ð¿:÷ÌcÏ!^uv´ÆX½PÞZíRN±åÿ‡L«U#iiM˜/euôÅcý}Ræûl5NÃIˆq£‘VÁ©KV=Öj¸GPéy“R:=SùbyÁÓ_©†›Òß£Ñè™¿\Çƒl£ÖúÑ'<¹ØÛ}Ò&ÑØ8€5!=¯ç½Ý‰ ãƒ#¢Gœg´qkŸŸ#]ôƒ"aœ©¨‰’!_ô ³¦©eW…5¥fAÔDŽNyæu¤ºœ{¥9B¶£ŠÊÉ±©ž=wGn*ãD-¥S b÷—£,ïs&%ƒ[•«¦œØq¸~g >Vª<üô˜ë<Aå“Õ¬ªg’œŽ"ÊÉ^ÝàÏ•žŒCuÊî à?„àHÊcH´“õDÅ©.ª\¯ÒWƒ©0Ž¦ªp©·…\ZW–ÂV”=}6Z>L$h'#}ìÁ± 
í;«dÚió1™À™ål½•ª‡½ï™-ÆÒjÏÞUI ±àW“£¤jîëÌÃg:Ð+°6U Wj—‚CÕ_M ZüLcþ¥?‘çf.ˆx”6cÄp,<ù0Ö%¡—É^ÌßÀ‘zGâüM“±/ëN‘àŽwÀ‰Z–Hç÷Åî`oðœ¾Ø>'\N€“¾¸ÌB[0oL¨×Ô_Å“I¢ïy.éªànkÁŽìâ·_ñ9ÁÙ»š¨ºÿ?U]ª¼ËQf2GÄÒTáªt€\_'SüÜÈ<À×{»Ï¿^ÿðíe/Ü šáYlŠ´Å5$B•¶©CV8–„?îXÁGáêµ‹“/¾E*H%'ï½<§/0 Š	¢•ýA%!:r´I	AiƒaÔí8|°èQ
ì{±·Ž}¿5ÆÉ-ù6õ/X@
ÆR¨T'Æ—‘„OBÔ¡`o]Ÿ%Õ™xç véV@.<-_#Ç9<dxþB%Ðqz$kY(§ê‘xG¤N%%8"ÑwºlNEÂW ×îÁëI¤<M™ëB;º7çL±4Ö£^X‹¿ÆQ6ÅôÃã:©U0ÐÔÆ*ˆÄ)	ÃƒÞ´IGâÅî“¯qNõÂ×ôéÐú±Ð9Ï{gDÒÜÜøscÎ3‘‰ò^úåõÈ˜âˆæþk“oÉ)ù7ô¼"ËåD<Ý¥(¸Gµ¤tÂp–¾Jµõ?w>6Ž01öu IëäÓä´|åœ»-Ÿî> “·'ÖË—ÎqÖÏ'\sá=p$*„wvÉ¸ó",yJ?ë¢P©Æcça \~ò ?~ˆùAz#áw²àvp¸¬;PŸ‚ÊáükL`š!ñ#³ÍØ—Î/é)‚–=­€­{£e>³Lß[Ãq6î-»¯Êî3ë*o9èÛM¢þ™¼ò©.#ÜyNâY Qã†³aÏ0~¹ÖXÏú¢j´jÅùQú@ë£PeÌFÛ‡ü]þ¬a‡ËªÒ%„m)rUŠœç K­†€éÚVÂvŒ[tÅkñÕe¨Í¶F}/Œú/Ùm[¶_Ííî²¯eçXDôÔµ^lÿ-±f±G *g­Kç'ÚÍ•žL·ôº‡ðw^ç}ÿ˜óÜ»ðÚ‰Oæ>€ts¥JàÃ)ï …
6bE÷°|<à£Å9ïEÈ‚RN>À¹ìÝá¼Î‰óÐâJ|BeOK;JM«>8önfy¤.à'Æ*#‘!™¤å’±LNb[öÕ_gu”Í&4Àèµ"_‹Asròð‚2JÔ§?ÿòì6q³4ß«9ü<\Æ‰ƒz¬]-	:üÞÎ¸ÓÜááÝ¡G"¡7ß­*…¥‡å¨ -,%tru™Á†ñ€¾m¢†yÇÌXMåLC\¯§eÿ!gF£\–%J Çmðåb’?#.‡jSD·áÕº¦±;”^$mã¯èIË¿J¦öu‡ÌEk®¨@ÔÃó7ãóc6!ðNüKgì4›±¼ÎkÊ¯)òkú¼ñ?8ÍÞÏuÄ¤gó²U*­
· ‡Í¹à¢){@¥uûá^	®Ë‹B{Ù£H}Rˆ{ c§”p¬Ú` RßÏ\ûšå¸PÑÿÉHð%Ð÷Š\Å¸Àj™äÖC&Ï@8í´qDxuç½³­¿ÔpÂù\.¨E‹V~!èøôøxä‹:d~ß¾}wðÓw?ú–­ùÔä½U¿pOjÇ8ŠÇ{¦º˜ŒW„Ä¢LcQÎTÁÿûÞ«Ú_{UµrM§mª)1Îý¤H÷m“ÆÍÎ’bR‹¡¯ð#5£½iGuì—ÂàÂ¬Ø?™ï‡'µÇñýV´66.‹£–öÄÁ‹æ‘—YÞ>’³ˆŸ5;^jÅqf?ÉÛNŒ­±6ßp$Þœ.ÏH{âi»(“°&p?ò“0áÚÔ/’úšï]àQà/¬sÕr{µ/U¹MµÆ†bÜt9\&uÎ5üYq¹A0ð TP“s˜ÑìíOVeMÎŠ^R:×'à]STQ#SLÚl™Á1[’ŒÀvQÖÓñâÁõ~rñÀ{pïSãø= ‰ø’CƒW8·Ñ ½ƒ©Tˆä«·DôäÎZWM©y+#j@;è§
¾ãºçÒý%¾WÏg(XâÆë@ÖOý@¨[TÜÌ‡â%îâI#Z5Ëvèý	¶«Ò”C^òMùhÒ« 0Îwûi£H·gú²ˆ|/GFt]S²|A#Ë(¶ŒFb.k¦Æ¾øµ‹[}Dë¥A…gÞÃ‹‡vÆ˜Zý¬ëjjúà¦v¯ìåÐwä±æõå]õó^à¶|[„[SÆ´Ÿ.Yùw HO5Üd§)k@ê\7	Ï‚á1ëÐsGž·ïhéØ~*{rº,áD©ŠÖMµh»O»üJ:_ö.ÇíôEœê?Ñ–UwÃªHëgÙ‘á:ˆïq9¼ozpðßÏ(ðçkoÀ©´Å{H÷¦Ò†:fê)õÝ tP-%w‡-–í§‹HŸãÍ&Ü8ïh‡ß‡ˆÔ.ªÄï%š’º³òü<ia™€›ä9ª÷ãŸÅ’J]œŠžióÓáâ5áO•‰¬¶,½–úÙßïÝAz­|–FêtµV§†RH×šÅ(õ®ñ9æR,ÍÔ6T.Ó¼GÕÐsÂ+L°Ü,ü:aìàHªÔÐ‡/Šù~ë3'uô#Ým½XsÐ½H@~RkßG¤NUBínay§ç¾.óV#ñ¶®MÝ½G@kLuÝT.ra!;éáqùre¬Û¢wuôdæ—Ço¼<Ö ¨·ÐÝ€á­fŒñTîÌî*F·Åíú&w°õ°Xon³Ûá™ZX–¸Ž™µýëLl‹ÕõÌëºPulbVæá•÷Êªº…½Ï6*SmdS[ ®eR×Â©Eàr xhåh[šÝmiv!p!²)ŽÛ*íN«´p¼RÖƒ‹âÝl›ãg™âÂ;©Ý64ÃÏ1Âmip»eÜfÆ·AqÐ‡zÛ­VtÜ•`Ûw·\Ümjj›WÛ:ï–ë¼ílfQz4øð^ì¹ÄæSí”­d¢†–ö¹ä-±Ú×yh+	Ým](¿b+“D“º)ËþëôRìDšÞéÊ…ƒ¥ ~Qßïf»ºÿªÖé›§¯¸ŸùÍ€6Âóû|Ñï¾«ÕÚñ.;M5°ŽÚÕšj§ÝW¦/Íï¢ê·ÀUâÚ^i»ÚšÒQ$ZÛ÷¶åÏ­òç°çJÜá<`Ë›[åëx#Î\À”HÜU8s.SÎÈj;l#\ÊSmérËt1Õ}gKD‚Ëµ—Õó»`JûŸ¸/mÍF©áô wFtLoøíÞíê»Ì2ú„ü«µkãG@m]Ûmšê·cÝâ{—øÊ„ gAû_ˆ§c„­:MòÆê™@[¸¢ZÒVÛe¾xÆ¨ý6Z³øž÷¨‹vÔ5aWªHo%O¥ïVåûa§*{`E‘#ñöTûÿŒ ¹ºjØ¨vùb‰ÿ¿zotÃž^á•uþBÌ%7wG¢r<šÓ…T§Ön¿§ó»ƒÅœñËñcE#E9zÙÃßGâÆê7ùŒ8oëÞ%óÛM{c~vûÞsöì]ŠØà½–­ýS¸ÑÿÝkX°k   0707010005c838000081b40000000000000000000000015ab2175400000ead000000660000000000000000000000000000003d00000000root/usr/share/doc/opensvc/template.service.fs.logfs.conf.gz  ‹      í]ksÛ6ýž_™LÆÎ¬¤µ³Íç1õn“Ì¶MgÝf?t:6D‚Ö$Á du»ÿ}Ï½ )P~HŽ±»ÒL[$/Á{Î}—ÈÓ§·ùyòTÜâ‡Äev Ü¢R"7“ÌÞXÜ-~ îvu÷äçÌ>ÝûåÉ+~Þ7þ‰Ÿ<¡aŸªÅÜÔéÁòîI­¤SÇ¦rÚ”§oñCŠ²‰©ä8WÝ=¬Ejõk£kµÌ{™[>RÕf¦-Æ£ËÉA|Iª2Ùän9üïMÉt9Uµv²LÂ}r%3ñVL•LÛA('Wµ?n+•èL'8g¢J\›Ði‰)gªŽäÛi®ÎXs¸·Mpà0M5©Jæ"èL8#*iù_7e‚é‡g&o
ÑE!ËtqçHÿ¦¾¥Äy²ÆŸ'§Â*‡óž¼êc$‡CŒÐéIc+—A:›ü_Ãõ#à`åšŒ¡Iµ=“Ú4ÿšé\Ù…uªÀ3(àmš<cÁÔ=†JEV›‚¡™M GfÌeX`ûEQh< [b•¬ C_¶è¬™U;x¨ºÆ9ÚF˜Äa„RgU­,_€ó_—oŸýçý?ß½ûý‡OG¿úû9>ñJìÀ…‡	]gY÷j@ãÜ$§­úI·‘éèBNÂ©±ƒ%4#h¢siašÒÄ7:ËT­J×JNd	»#ß'`ž@0mÏˆD•7¡k¿¦ïØÈí¢t’¼õ+|}5Ä¿y}}9Œ/w’_dRˆw•ì-FåSL‰¡2h0€¢2kS«J^ð“8æƒ!É­¤›ŠÚ—Y¶@¾×•ðú—CçY`¬ñ9Ä«ÎŽVÂ«Ê[«]Ê)¶ü?§âiµj$-­	ó¥¬Ž¿x¬HÊü­Æi8	1n4Ò*8uÉ*£ÇZ÷*=oRJ§g*_,/Øý7q³3ú{4=÷7‚kâ˜cmÔšÂC?ú„'û{ÏÚD#g°&¤çUã¼÷£"t|pDôˆóŒö níós¤‹~C„!Œ35"Q2äê‹~`Ö4u¢ìª°¦Ô,è;5‘ã…Sžy©®æ^iŽ‘í¨¢rÇrlêÇgÏÝ‘ÛÊ8QKéˆØƒåhÅïËûœKIÇÆàVå*)'v®ßˆ•*>ýMÌuž‹ rŒÉjVÕ3INGåd¯nðg†ÊO	Æ¡º	ewðBp$Žå1$ÚÉz¢âÔGU®
„Wé«ÁÔ€GSU¸ÔÛB.­ƒ+Ka+Êž=-&´“‘>vÈàX …öU2í´ù˜L`†Ìr¶ÞJÕÃ^È÷Ìciµgïª$Øp	«ÉqR5uæá3è5X›*Ð+µKÁ¡ê¯&-~¦1ÿÒŸÈs3D<J›1b8ž|ë’P‚Ëd/æïàH½#ñ7üM“±/ëN‘àŽwÀ‰Z–HçÄÞ`ð‚¾Ø¾ \N€“¾¸ÌB[0ïL¨×Ô_Å“IßEßó\ÒuÁ%ÜÖ‚Û)Äo!¾â‚²w5Quþ¨ºTy—£ÌdŽˆ¥©ÂUé ¹¾N¦ø¹‘9x€¯÷÷^|%¼þáÛË^¸4Ã1²Øi‹k8H„*mS‡¬&p,5 9~Ü±‚ÂÕk'_|ŠTJNÞ{yN_("`@	D#*ûƒJ(Btäh?’‚ÒÃ¨Ûqø`Ñ£Ø÷rû~mŒ“[òmê_°€Œ¤ P©þNŒ/#	Ÿ„&¨CÁÞº ?Kª3ñ,ÎAìÒ­€\xZ¾FŽ-rxÈðü…$J:0 sãôHÖ²PNÕ#ñŽHIJpD¢ouÙœ‰„¯@®?Ü‡×“Hyš2×…vtoÎ™bi¬G½°:!£lŠ1è‡Ç/tR«` ©U‰S†½i“ŽÄË½g_ãœ ë¥¯#èÒ¡õc¡s^ôÎˆ¤¹¹ñçÆœg"å½ô«#ê±1Å1Íý×&ß’ÿJòoèyE–Ë‰ØÝ£(¸Oµ¤tÂp–¾Jµõ?w>6Ž01öu IëäÓä´|åœ»-w÷žÉÛSëåKç8ëç‚®¹ð8Â;»dÜy–…¼¥ŸuQ¨Tã±ó0.?y¿‹ùAz#áw²àvp¸¬;PŸ‚ÊáükL`š!ñ#³ÍØ—Î¯è)‚–=­€­û£e>³Lß_Ãq6î-»¯Ëîsë*ï8èÛM¢þ¹¼rW—î<'ñ<¨qÃÙ°g?È\ë	¬‹g}Q5Zµâü(} õQ¨2æ£íCþÖ°ÃÎeUéÂ¶¹.E.r€G€NŠ¥VCÀôm+a;Æ-ºâøê*Ôf[£~Fý'‚ì®-Û¯ævwÙWƒ²s¬@"zêÚ/¶ÿ–X³Ø#P•³Ö%ŒóSmŽçJO¦[zÝŠCø+¯ó~øˆGÌyî]xíŽÄ'ó @º¹R%ðá”w B±¢{X>ðÑâœ÷2dA)'á\öÞ¥pÞBçÄEhq%>¡²Ž§¥%¦U{73‹<Rðc•‘‡ÈLÒrÉX&§±-ûê¯À³:Êfš?`ôZ‘oÄ¥ 99y|A%êîÏ¿<¿KÜ,Mà÷j?—±ÆFâ°kWK‚¿·3î4÷Cxxwè‘CèÍãw«Jaéa9*HK	\]f°a< o›¨aÞq3VS9Ó×+ÆiYÀßGÈ™Ñ(—e‰ÀqC|ù„˜äÏˆË¡ÚÑmxu®iì¥IÅø+zÒòÏ’©Æ£}Ó!sÙš+*õø<Äíøü˜M¼?ÇÒ;Íf,¯óšòkŠüãš¾ oüN³sñéÙ¼l•J«Â-HÁasn¸hÊPéDã_Å~¸W‚ëòÔ¢Ð^ö(RŸâÈØ)%«6¨Ô÷3×¾f9.Tô0|	ô½"Wq.°Z&¹õÉ3PN;mQ'^Ýùàlë/5œp>—jÑ¢•_:9=9ù¢™ß7ïÞþôí¾ek>5yoÕ/Ü‚Ú1Žâñž«.¦ã!±(ÓX”3Uðÿ¾·Áªö×ÞDU­\Sãi›„jJŒó )ÒƒÛ¤q@³³¤˜ÔbhÅküHÍhoÛQø¥0¸0+Nç'á	Gíq<A¿­ËÇâ¨¥ý#qð¢yäå@–·ä,âgÍN–Zñ_œ‡ÙOò¶ck¬Í7ÉÇ7§ËFÁsÒžzÚ.Ê$¬	<L‡üƒAÀ$L¸6õ‹¤¾Åƒæ{CWxøë\µÜ^íKUnS­±á7]—Is^\n|'Ôäf4{û“UY“³¢—”Îõ)x×UÔÈ“6[fpÌÅ–d ã°]–uÅt¼xtD}˜\<ôÜûÔ8~h"¾äÐàÎmC4hï`E*"ùjã­=¹³ÖUSjÞÊˆÐû©‚ï¸î¹t‰ïÕóY#
–¸ñ:ƒ5ÆS?ê7ó¡x‰ûƒxÒˆVÁ²z‚íª4å—|S~#šô*(ŒóÆ~Ú(’Ãí™¾,"ßKÆ‘Qã]×”€,_ÐÈ2Š-£‘˜Ëš©q ¾AíâVÑßziPá™Âðâ¡3¦V?ëºšZ†>:ƒi…=({9òyì£y}ùTWý¼¸-ß!ÇÖ”1í§KVþ(ÒS7ÙiÊ:×MÂ³`xÌ:ôÜ‘çí;ÚB:6ŸÊžœ.K8Uª¢µAS-ÚîÓ.¿’Î—½Ëq{}§úO´eÕý°*ÒúyöDd¸	â{\Íï›ü3
üùÚ[p*mñÒ½©´á…Î‚‚zJ}7ÔEKÉÝQ‹eûé"ÒÀçx³	7Î;ÇÑá÷!"µ‹*ñ{‰¦¤î¬Ã<¿HZX&à&yŽêýøçBq†¤RW§¢çÚÁü4B¸xMøSe"«-Kï‚¥~öÀ÷{w†^+Ÿ¥QÇ…:[­Õ©áD§ÔÒµf1J½k|Ž¹K3µ•Ë4ïQ5ôœð
,7¿N;8’*5ôá‹b¾ßúÌI?æHwW/Öv/P†ŸÔÚ÷©3•P»[XÞé¹¯«¼ÕH¼«kSwïÐS]7•‹\XÈNzx\=…\ë¶è]=™ùåñ[o5èê-t·`x«c<•{³»ŽÑmq»¹ÉÝl=,Ö›Ûãìvx@¦–%nbfmÃ:Ûbu3óº)T›˜•y|åÇƒ²ªnaï³ÊTÙÔ¨™Ôpj¸(Þ Z9Þ–f÷[š]
ÜeˆlŠã¶J»×*m¯U§õàß¢x?ÛfÆøY¦¸…ð^j·ÍðsŒp[Üm·™ñmPô¡ÞÂv§Ý†w-Ø¶ÅÝw›šÚæUÃ¶Î»ã:oc;[‡Y”>¾{î±ùT;e+™¨¡¥}.yK¬öuÚJBw›AÊ¯ØÊ$QÀ¤nÊ²ÿ:½;‘¦wºráp)ˆ_Ô÷»Ù®.Æ¿®uúv÷5÷3¿ÐFx~Ÿ/úÝwµšB;Þ%c§©ÖQ»ZSí´ûÊô¥ù]Tý6 ¸¡J\Û+mW[S:J€DkûÞ¶ü¹Sþ<ö\‹;œlys§¼aoÄ™K˜‰»g.dÊ9Ym‡mDƒ+ùbª-]î˜.¦zèl‰Hpu`¢öòãz~Lé`ÿ÷¥­Ù(5¼‘ôÎˆŽém ¿Ý»]}—Y¦C¿õ¡vmü¨­k£MSýv¬[|ï_™°à,h¿ãáK±Ë0FØª³$o¬ž©´5+ª%mµ]æ‹çŒÚ_h£5‹ïyºhG]v¥ŠdñVòT*ñnU¾vªò°VÔ9ïÎ´ÿÏ"‘« †j—/–øÿë ÷F×0ìé^Yç/Ä\rsw$*Ç£9]È@ujíöÛp:¿k0XÌÉ¿?V40R”£—}ð0ü}$nlð ~“Ïˆ‹¶î]2¿Ý´7öáç·ï½`ÏÞ¥ˆÞkÙºÑ?„ýî°u±k     0707010005c839000081b40000000000000000000000015ab2175400000eaf000000660000000000000000000000000000003d00000000root/usr/share/doc/opensvc/template.service.fs.minix.conf.gz  ‹      í]ksÛ6ýž_™LÆÎ¬¤µ³Íç1õn“Ì¶MgÝf?t:6D‚Ö$Á du»ÿ}Ï½ )P~HŽ±»ÒL[$/Á{Î}—ÈÓ§·ùyòTÜâ‡Äev Ü¢R¢Ð¥>»±¸[ü@ÜíêîÉÏ™}º÷Ë“Wü¼oü?yBÃ>U‹¹©ÓƒåÝ“ZI§ŽMå´)-NÞâ‡eSÉq®º{þX7ŠÔê×F×j9˜÷2·|¤ªÍL[ŒG—“ƒø’Te²ÉÝrøß›’èrªjíd™„ûäJfâ­˜*™¶ƒPO®jÜV*Ñ™NpÎD•¸6¡ÓSÎTÉ·Ó\±æpo›àÀašjR•ÌEÐ™pFTÒò¿nªDn&:Áá™É›BCtQÈ2EÜ9Ò¿)ïD)qž¬ñ'ÇÉ©°Êá¼'¯úØ Éá#tzÒ˜ÆŠÅeÎ&ÿ×0F`ý8X¹&chRmOÅ¤6MÅ¿f:Wva*pÁL'
x›&OÅXE0u¡R‘Õ¦`hfÀ‘sØ~QÚÈ–X%+èÐ—-:+†CfÕª®qŽ¶f#q¡Ä‚ÔYU+Ëàü×åÛgÿyÿÏwï~ÿáÓÑïŸþþ_FŽO¼;páaB×YÖ½Ð87Éik ¤~Òmd:º“p`j¬Ã`	Íšè\`Z˜¦t#ñÎ2U«Òµ’YÂîÈ÷	˜'LÛ3"Q¥ÁÀDcÃMèÚ¯é;6r»($oý
__ño^__ãËä—™â]%;F‹CùÓFâCF¨Ú# ¨ŒÆßÚFàÔª’„ü$Žù`Hr+é¦¢6Æe–-ïu%<ƒþåÐy`k|ñª³£•0Æê…òÖj—rŠ-ÿÏ©8dZ­IKkÂ|)«ã/ë’2?d«qNBŒ´
N]²Êè±VÃ=‚JÏ›”Òé™ÊËvÿMEÜìŒþFÏýàš8ædµ¦ðÐ>áÉÅþÞ³6ÑˆÆÆÙ ¬	éyÕ8ïýè†H=â<£=ˆ[ûüé¢äaãLEH”¹ú¢Ÿ ˜5M(»*¬)5úNMäxá”g^Gª«¹Wšcd;ª¨Ü±›úñÙswä¶2NÔR:"ö`9Zñûò>çRÒ±1¸U¹J`ÊAÀ‰‡ëwâc¥Ê£Osç"¨c²š€UõL’ÓÀQD9Ù«ü™¡òÃS‚q¨î@BÙ¤ü‡‰#Cy‰v²ž¨8õÑE•«áUúj05` ÆÑT.õ¶KëàÊRØŠ²gÏGË‡‰íd¤28@¡}g•L;m>&˜!³œ­·Rõ°ò=³ÅXZíÙ»*	$ö \BãjrœTÍCyøLzÖ¦
ôJíRp¨ú«	D‹ŸiÌ¿ôg òÜÌÒfŒŽ…'Æº$”à2Ù‹ù»8RïHüÓ$CìËºS$¸£Áp¢–%Òù±7Ø¼ /ö†/×€à¤/.³PÅÌ»ê5õÂBñdÒwÑ÷<—t]p	·µà‚Çv
ñ[ˆ¯†ø‚`ƒì]MTÝƒÿª.UÞå(3™#biªpU:@®¯“)~ndàëý½_	¯øö²n ÍpŒ,6EÚâ¡JÛÔ!«	KBŽ÷@¬à£põÚÅÉßƒ"¤’“÷^žÓŠE‚ÑˆÊþ Š9Ú$‚ƒ„ ´Á0êv>Xô(ö½Ü_Ç¾_ãä–|›ú, ã)Tª¿ãËHÂ'¡	êP°·.ÈÏ’êL<‹s»t+ ž–¯‘c‹2<!‰’èœÀ8=’µ,”SõH¼‡#Rg’œ‘è[]6g"á+ë÷áõ$Rž¦Ìu¡Ý›s¦XšëQ/¬ŽÅ_Hã(›búáñÔÆ*hjcDâ”„áAoÚ¤#ñrïÙ×8'Èzéëú‚thýXèœ½3"innü¹1ç™ÈDy/ýêˆzlLqLsÿµÉ·ä¿’üzÞC‘år"v÷(
îS-)ðœ¥¯RmýÏÄ#ÌCŒ=E@Ò:ù49-_9çnÁÝ½çdòöÔzùÒ9Îú¹à„k.¼ŽD…ðÎ.w^„e!ïCég]*Õxì<„ËOäÇï"A~ÞcãHø,¸.kãÔ§ ²Ex?Á˜fHüÈl3ö¥ó+zŠ %DD+`«Äþh™OÄ¬Ó÷×pœ{Ëîë²ûÜºÊ;úv“¨.¯ÜÕe„;ÏI<$jÜðc6ìÙ Æ2×zëâY_TV­8?Jh}ªŒ¹Áhû¿ÇŸ5ì°sYUº„°-E®K‘‹à “b©Õ0ýBÛJØŽq‹®x#¾º
µÙÖ¨„Qÿ‰ »kËö«¹ÝCöÕ ì+ˆžºöÃ‹í¿%Ö,öTå¬u	ãüT›ã¹Ò“é–^·âþÊë¼>âsž{^»#ñ‰À< n®T	|8åÝ@£PÁF¬è–|c´8ç½YPÊÉG8—½w)œ·Ð9qZ\‰O¨¬ãiiGI£iÕÇÞÍÌ"ÔüÄXeä!2$“´\2–ÉilË¾ú+ð¬Ž²Ù„æ½Väq)hNN_PF‰ºûó/Ïï7Kø½šÃÏÃe¬±‘8¬ÇÚÕ’ ÃïíŒ;ÍýÞz$ÂzóøÝªRXzXŽ
ÒÂRB'W—lèÛ&j˜w\ÁŒÕTÎ4ÄõŠqZð÷ßrf4ÊeY¢pÜÐ_>!&ù3âr¨6Et^] k»CéEÒF1þŠž´ü³dªñhßtÈ\¶æŠ
D=>q;>?fïÄÏ±tÆN³Ëë¼¦üš"ÿ¸¦/Àÿ£ÓìÃ\G<Dz6/[¥ÒªpRpØœ[.š²T:ÑøWg±î•àº<µ(´—=ŠÔ'E¸Ç 2vJ	Çª*õýÁµ¯YŽýŒ_}¯ÈUÜ¬–	An=dòT€ÓNGÔ‰Ww>8ÛúK'œÏå‚Z´hå‚NNàAONF¾¨Cæ÷Í»÷‡?}û£oÙšOMÞ[õ÷„ vŒ£x¼çª‹©ÁxEH,Ê4åLü¿ïm°ªýµ7QU+×ÔxÚ&¡šã<HŠôàÄ6iÐì,)&µZñ?R3ÚÛvT'~).ÌŠƒÓùÁIxÂQ{OÐoEkcãò±8jiÿH¼hy9åí#9ËøY³“¥Vüçaö“¼íÄØkóGòñÍé²Qð\´§ž¶‹2	kÓ!ÿ`0	®Mý"©oñ ùÞÐþÂ:W-·WûR•ÛTklx ÆM—ÃeRç\ÃŸ—ß	B59‡ÍÞþdUÖä¬è%¥s}
Þ5E52Å¤Í–s±%È¸l—e]@1/Q&½÷>5Žßšˆ/94x…sÛÚ;X‘J…H¾ÚxkADOî¬uÕ”š·2¢´Ã~ªà;®{.Ý_â{õ|Öˆ‚%n¼ä`ñÔ„ºEÅÍ|(^âþ ž4¢UC°l‡ÞŸ`»*M9ä%ß”ßÈ€&½

ã|‡±Ÿ6Šäp{¦/‹È÷’qdÔ8A×5% Ë4²ŒbËh$æ²fjˆoP»¸ÕGô·^Txæƒ0¼xhçŒ©ÕÏº®¦–¡Î`ZaÊ^Ž|Gûh^_>ÕU?ïnË·EÈ±5eŒAûé’•ŠôTÃMvš²¤Îu“ð,³=wäyûŽ¶ŽMà§²'§ËN•ªhmÐT‹¶û´Ë¯¤óeïrÜÞA_Æ©þmYu?¬Š´~ž=nÂ€øWsÀû¦GÿÃŒÂ¾öœJ[¼‡to*mx¡s`† žRßBuÑRrwÔbÙ~ºˆ4ð9ÞlÂóŽÆqtø}ˆHí¢Jü^¢)©;ë0Ï/’–	¸Iž£z?þyPœ!©ÔÁ©è¹v0?.^þT™ÈjËÒ»`©Ÿ=ðýÞ„¡×ÊgiÔq¡ÎVkuj8Ñ)µ€t­YŒRïŸc.ÅÒLmcAå2Í{T='¼ÁËÍÂ¯ÆŽ¤J}ø¢˜ï·>sRÇ9ÒÝÕ‹5‡Ý‹”á'µö}DêL%Ôî–wzîë*o5ïêÚÔÝ{´ÆT×Må"²“WO!WÆº-z×GOf~yüÖÁ[Ácz€zÝ-ÞjÆOå^Ãì®ct[Ünnr·[‹õæö8»©…e‰›˜YÛß°ÎÄ¶XÝÌ¼n
U‡Á&fe_ùñ ¬ª[Øûl£2ÕF6µêF&u#œZ®Š7€VŽ·¥Ùý–f—w"›â¸­ÒîµJÛ ÇkÕi=ø·(ÞOÁ¶™1~–)n!¼—ÚmC3ü#Ü–w[Æmf|}¨·°ÝiE·¡Á]¶mqwÇÅÝ¦¦¶yÕ°­óî¸ÎÛØÎÖae ÇAƒïÅž»Al>ÕNÙJ&jhiŸKÞ«}‡¶’ÐÝfÐ…ò+¶2I0©›²ì¿N/ÅN¤é®\8\
âõýn¶«‹ñ¯k¾Ý}ÍýÌo´žßç‹~÷]­¦ÐŽwÉØiªuÔ®ÖT;í¾2}i~U¿ n¨×öJÛÕÖ”Ž ÑÚ¾·-î”?„=×âç[ÞÜ)oXÇqæ¦Dâ®Ã™™rNVÛaÑàJ¾˜jK—;¦‹©:["\˜¨½ü¸žßS:ØÿÀ}ik6Jo¤½3¢czÀo÷nWße–éÐo ä_}¨]?jëÚÆhÓT¿ëßûÄW&¬ 8ÚïxøRì2Œ¶ê,É«gj mMàŠjI[m—ùâ9£öÚhÍâ{Þ£.ÚQ×„]©"Y¼•<•J¼[•ï‡ª<ìuDŽÄ»3íÿ3‚Häê¨a£Úå‹%þÿ:è½Ñ5{z…WÖù1—ÜÜ‰ÊñhN2PZ»ý6œÎïsrÄ/ÇŒåèe<‰<¨ßä3â¢­{—Ìo7í}øùí{/Ø³w)bƒ÷Z¶nôáFÿÜ¥šp±k   0707010005c83a000081b40000000000000000000000015ab2175400000eaf000000660000000000000000000000000000003d00000000root/usr/share/doc/opensvc/template.service.fs.msdos.conf.gz  ‹      í]ksÛ6ýž_™LÆÎ¬¤µ³Íç1õn“Ì¶MgÝf?t:6D‚Ö$Á du»ÿ}Ï½ )P~HŽ±»ÒL[$/Á{Î}—ÈÓ§·ùyòTÜâ‡Äev Ü¢R¢°©±7w‹ˆ»]Ý=ù9³O÷~yòŠŸ÷â'OhØ§j17uz°¼{R+éÔ±©œ6¥Å)Ã[ü¢lb*9ÎUwÏëFÑZýÚèZ-ó^æ–Tµ™i‹ñèrr_’ªL6¹[ÿ{Sò]NU­,“pŸ\ÉL¼S%ÓvJàÉUíÛJ%:Ó	Î™¨×&tZbÊ™ª#ùvš«3Öîm8LSMª’¹:ÎˆJZþ×M•ÈÍD'8<3yS¨ `ˆ.
Y¦£ˆ;Gú7%ð(%Î“5þä89V9œ÷äU 9b„NOÓX±¸ÒÙäÿÆ¬+×dMªí©˜Ô¦©ø×LçÊ.¬S.˜éDoÓä©«¦î1T*²ÚÍl82c.ÃÀÛ/ŠB{àÙ«dú²EgÅpÈ¬ÚÁCÕ5ÎÑ6Âl$#”X:«jeùœÿº|ûì?ïÿùîÝï?|:úýÓßÿËÈñ‰Wb.<Lè:ËºWç&9m„ÔOºLGrLu,¡ALÓ”n$¾ÑY¦jUºVr"KØù>ó‚i{F$ª4˜hl¸	]û5}ÇFn¥“ä­_áë«!þÍëëËa|¹“ü2 “B¼«dÇh1b(ŸbÚH|È•A{„•Ñø[ÛœZU’ð‚ŸÄ1In%ÝTÔÆ¸Ì²ò½®„§`Ð¿:ÌcÏ!^uv´ÆX½PÞZíRN±åÿ9‡L«U#iiM˜/euüÅcýCRæ‡l5NÃIˆq£‘VÁ©KV=Öj¸GPéy“R:=SùbyÁî¿©ˆ›Ñß£Ñè¹¿\Çƒl£ÖúÑ'<¹Øß{Ö&ÑØ8€5!=¯ç½Ý‰ ãƒ#¢Gœg´qkŸŸ#]ôƒ"aœ©¨‰’!W_ô ³¦©eW…5¥fAß©‰/œòÌëHu5÷JsŒlG•;–cS?>{îŽÜVÆ‰ZJ§@Ä,G+~_Þç\J:6·*W	L98±ãpýÎ@|¬Tyôéob®ó\•cLV°ªžIr8Š('{uƒ?3T~xJ0ÕH(»ƒt€ÿ‚#qd(!ÑNÖ§>º¨rU ¼J_¦À8šªÂ¥Þri\Y
[Qöìùhù0‘ Œô±CÇ(´ï¬’i§ÍÇd3d–³õVªöB¾g¶K«={W%Ä€Kh\MŽ“ªy¨3Ÿé@¯ÁÚT^©]
U5hñ3ù—þDž›¹ âQÚŒÃ±ðäÃX—„\&{17 Gê‰¿áošdˆ}YwŠw4¸NÔ²D: öûƒôÅÞðápœôÅå`ªØ‚yw`B½¦^øB(žLú.úžç’®.á¶\0àØN!~ñÕ_l½«‰ª{ðÿCÕ¥Ê»e&sD,M®JÈõu2ÅÏÌÁ|½¿÷â+áõß^öÂ Ž‘Å¦H[\ÃAb Ti›:d5c©AÈñãˆ|®^»8ùâ{P¤‚TròÞËsúB¢H QÙTB¢#Gû‘Dp”6FÝŽÃ‹¥À¾—ûëØ÷kcœÜ’oSÿB€¤`ü Jõwb|Iø$4A
öÖùYR‰gqb—näÂÓò5rl‘ÃC†ç/$QÒ§G²–…rª‰÷pDêLR‚3 }«ËæL$|rýá>¼žDÊÓ”¹.´£{sÎK3`=ê…Õ±øieSŒA?<~¡“ÚXMm¬‚Hœ’0<èM›t$^î=ûçY/}A_­ó¢wF$ÍÍ?7æ<™(ï¥_Q)Žiî¿6ù–üW’CÏ{(²\NÄîEÁ}ª%¥¾€³ôUª­ÿ™¸ó±q„yˆ±§¨HZ'Ÿ&§eà+çÜm!¸»÷|€LÞžZ/_:ÇY?œpÍ…÷À‘¨ÞÙ%ãÎ‹°,ä}(ý¬‹B¥‡pùÉƒüø]$ÈÒ{l	¿“·ƒÃemÜúT¶Ïà'XcÓ‰™mÆ¾t~EO´„èhl•Ø-ó‰˜µ`úþŽ³qoÙ}]vŸ[WyÇAßnõÏå•»ºŒpç9‰çD~Ì†=ÀøAæZO`]<ë‹ªÑªçGé­B•17mò÷ø³†v.«J—¶¥Èu)r‘<tR,µ¦_h[	Û1nÑoÄWW¡6Ûõƒ0ê?dwmÙ~5·»sÈ¾”cÑS×~x±ý·ÄšÅªœµ.aœŸjs<Wz2ÝÒëVÂ_y÷ÃG<bÎsïÂkw$>˜ ÒÍ•*§¼û h*ØˆÝÃòñ€oŒç¼—!J9ùç²÷.…ó:'.B‹+ñ	•u<-í(i4­úàØ»™Yä‘º€Ÿ«Œ<D†d’–KÆ29mÙWžÕQ6›Ðü£×Š|#.ÍÉÉãÊ(Qwþåù]âfi¿Wsøy¸Œ56‡õX»Ztø½q§¹ÂÃ»CDBo¿[U
KËQAZXJèäê2ƒã}ÛDóŽ+˜±šÊ™†¸^1NËþþ;BÎŒF¹,K” ŽÚàË'Ä$F\Õ¦ˆnÃ«tMcw(½HÚ(Æ_Ñ“––L5í›™ËÖ\Q¨Çç!nÇçÇlBàø9–ÎØi6cy×”_Sä×ôxãtš}˜ëˆ‡HÏæe«TZnA
›s+ÀESö€J'ÿê,öÃ½\—§…ö²G‘ú¤(÷@ÆN)áXµÁ@¥¾Ÿ!¸ö5Ëq¡¢ÿƒ‘àK ï¹Š;pÕ2!È­‡Lž
pÚiãˆ:ñêÎg[©á„ó¹\P‹­üBÐÉ	<èÉÉÈuÈü¾y÷þð§oô-[ó©É{«~ážÔŽq÷\u15¯‰E™Æ¢œ©‚ÿ÷½Vµ¿ö&ªjåšOÛ$TSbœI‘œØ&š%Å¤C+^ãGjF{ÛŽêÄ/…Á…Yqp:?8	O8jã	ú­hml\>G-í‰ƒÍ#/²¼}$g9?kv²ÔŠÿâ<Ì~’·[cm¾áH>¾9]6
ž+öÔÓvQ&aMàa:ä&aÂµ©_$õ-4ßº"À£À_Xçªåöj_ªr›jÄ¸ér¸Lêœkøóârƒ`à;A¨ &ç0£ÙÛŸ¬Êšœ½¤t®OÁ»¦¨¢F¦˜´Ù2ƒc.¶$7€í²¬(¦ãÅ£#êÃäâ¡÷àÞ§Æñ{@ñ%‡¯pn¢A{+R©ÉWo-ˆèÉµ®šRóVFÔ€vØO|ÇuÏ¥ûK|¯žÏQ°Ä×¬1žúP·¨¸™ÅKÜÄ“F´j–íÐûlW¥)‡¼ä›òÐ¤WAaœï0öÓF‘nÏôeù^2ŽŒ'èº¦dù‚F–QlÄ\ÖLñj·úˆþÖKƒ
Ï|†íœ1µúY×ÕÔ2ôÑL+ìAÙË‘ïÈcÍëË§ºêç½Àmù¶9¶¦Œ1h?]²ò¯@‘žj¸ÉNSÖ€Ô¹nžÃcÖ¡çŽ<oßÑÒ±	üTöätYÂ©R­šjÑvŸvù•t¾ì]ŽÛ;èË8Õ¢-«î‡U‘ÖÏ³'"ÃMßãjxßôèà˜Q8àÏ×Þ‚Si‹÷îM¥/t.ÌÔSê»Aè .ZJîŽZ,ÛO‘>Ç›M¸qÞÑ8Ž¿©]T‰ßK4%ugæùEÒÂ27ÉsTïÇ?/Š3$•º"8=×æ§ÂÅkÂŸ*YmYz,õ³¾ß»ƒ0ôZù,:.ÔÙj­N':¥®5‹Qê]ãsÌ¥Xš©m,¨\¦yª¡ç„W ˜`¹YøuÂØÁ‘T©¡_óýÖgNêø1Gº»z±æ°{‘€2ü¤Ö¾H©„ÚÝÂòNÏ}]å­Fâ]]›º{€Ö˜êº©\äÂBvÒÃãê)äÊX·EïúèÉÌ/ß:x+x¬APo¡»Ã[Íã©Ük˜ÝuŒn‹ÛÍMî6`ëa±ÞÜg·Ã2µ°,q3kûÖ™Ø«›™×M¡ê0ØÄ¬Ìã+?”Uu{ŸmT¦ÚÈ¦¶@ÝÈ¤n„S‹ÀÕ@ñ0ÐÊñ¶4»ßÒìRà.CdS·UÚ½Viàx­:­ÿÅû)Ø63ÆÏ2Å-„÷R»mh†Ÿc„ÛÒànË¸ÍŒoƒâ õ¶;­è64¸kÁ¶-îî¸¸ÛÔÔ6¯¶uÞ×yÛÙ:Ì¢ô8hðñ½Øs7ˆÍ§Ú)[ÉD-ísÉ[bµ¯óÐVºÛºP~ÅV&‰&uS–ý×é¥Ø‰4½Ó•‡KAü¢¾ßÍvu1þu­Ó·»¯¹Ÿùí€6Âóû|Ñï¾«ÕÚñ.;M5°ŽÚÕšj§ÝW¦/Íï¢ê·ÀUâÚ^i»ÚšÒQ$ZÛ÷¶åÏòç‘°çZÜá<`Ë›;åëx#Î\Â”HÜu8s!SÎÉj;l#\ÉSmérÇt1ÕCgKD‚«µ—×óû`Jû¸/mÍF©áô wFtLoøíÞíê»Ì2ú„ü«µkãG@m]Ûmšê·cÝâ{ŸøÊ„ gAû_Š]†1ÂV%ycõL ­	\Q-i«í2_<gÔþB­Y|Ï{ÔE;êš°+U$‹·’§R‰w«òý°S•‡=°¢ŽÈ‘xw¦ýF‰\Ý 5lT»|±Äÿ_½7º†aO¯ðÊ:!æ’›»#Q9ÍéBªSk·ß†Óù]ƒÁbNŽøåø±¢‘¢½ìƒ‡áï#qcƒõ›|Æ@\´uï’ùí¦½±?¿}ï{ö.Elð^ËÖþ!Üèÿ ½¬±k   0707010005c83b000081b40000000000000000000000015ab2175400000eaf000000660000000000000000000000000000003d00000000root/usr/share/doc/opensvc/template.service.fs.nilfs.conf.gz  ‹      í]ksÛ6ýž_™LÆÎ¬¤µ³Íç1õn“Ì¶MgÝf?t:6D‚Ö$Á du»ÿ}Ï½ )P~HŽ±»ÒL[$/Á{Î}—ÈÓ§·ùyòTÜâ‡Äev Ü¢R¢Ôyfo,î?w»º{òsfŸîýòä?ïÿÄOžÐ°OÕbnêô`y÷¤VÒ©cS9mJ‹S†·ø!EÙÄTrœ«îž?Ö¢µúµÑµZæ½Ì-©j3ÓãÑåä ¾$U™lr·þ÷¦äºœªZ;Y&á>¹’™x+¦J¦í ”À“«Ú·•Jt¦œ3Q%®Mè´Ä”3UGòí4Wg¬9ÜÛ&8p˜¦šT%st&œ•´ü¯›*‘›‰Npxfò¦PAÁ]²LGwŽôoJà;QJœ'küÉqr*¬r8ïÉ«>6@r8Äž4¦±bq¤³Éÿ5ŒX?V®ÉšTÛS1©MSñ¯™Î•]X§
\0Ó‰Þ¦ÉS1VLÝc¨Tdµ)šÙpdÆ\†¶_…öÀ²%VÉ
:ôe‹ÎŠáYµƒ‡ªkœ£m„ÙHF(± uVÕÊò8ÿuùöÙÞÿóÝ»ßøtôû§¿ÿ—‘ã¯Ä\x˜Ðu–u¯4ÎMrÚ©Ÿt™Ž.ä$˜ë0XB3‚&:˜¦)ÝH|£³LÕªt­äD–°;ò}æ	ÓöŒHTi00ÑØpºökúŽÜ.J'É[¿Â×WCü›××—Ãør'ùe@&…xWÉŽÑbÄP>Å´‘ø*ƒö(*£ñ·¶8µª$á?‰c>’ÜJº©¨q™eä{]	OÁ 9t˜ÆŸC¼êìh%Œ±z¡¼µÚ¥œbËÿs*™V«FÒÒš0_Êêø‹Çú‡¤ÌÙjœ†“ãF#­‚S—¬2z¬Õp Òó&¥tz¦òÅò‚ÝS7;£¿G£Ñs#¸&Ž9ÙF­)<ô£Oxr±¿÷¬M4¢±q6 kBz^5Î{?º!AÇGD8ÏhâÖ>?Gºè9DÂ8SQ#%C®¾è' fMS'Ê®
kJÍ‚¾S9^8å™×‘êjî•æÙŽ**w,Ç¦~|öÜ¹­Œµ”Nˆ=XŽVü¾¼Ï¹”tlnU®˜rpbÇáúøX©òèÓßÄ\ç¹*Ç˜¬&`U=“ä4pQNöêf¨üð”`ª;Pvé ÿ!GâÈPC¢¬'*N}tQåª@x•¾L€q4U…K½-äÒ:¸²¶¢ìÙóÑòa"A;éc‡ŽPhßY%ÓN›ÉfÈ,gë­T=ì…|Ïl1–V{ö®J‰= —Ð¸š'UóPg>Ó^ƒµ©½R»ªþjÑâgó/ýˆ<7sAÄ£´#†cáÉ‡±.	%¸Löbþn ŽÔ;Ãß4Éû²î	îhpœ¨e‰tþ@ìö/è‹½áÂ5à8é‹ËÁ,T±óîÀ„zM½ð…P<™ô]ô=Ï%]\Âm-¸`À±Büâ«!¾ Ø {WU÷àÿ‡ªK•w9ÊLæˆXš*\•ëëdŠŸ™ƒøzïÅWÂë¾½ì…@3#‹M‘¶¸†ƒÄ@¨Ò6uÈjÇR‚ãÇ=+ø(\½vqòÅ÷ H©ää½—çô…"D‘ A4¢²?¨„"DGŽö#‰à !(m0Œº‡=J}/÷×±ï×Æ8¹%ß¦þ… HÁøA
•êïÄø2’ðIh‚:ì­ò³¤:ÏâÄ.Ý
È…§åkäØ"‡‡Ï_H¢¤:'0Nd-åT=ïáˆÔ™¤g@$úV—Í™Hø
äúÃ}x=‰”§)s]hG÷æœ)–fÀzÔ«cñÒ8Ê¦ƒ~xüB'µ±
šÚX‘8%axÐ›6éH¼Ü{ö5Î	²^ú:‚¾ Z?:çEïŒHš›nÌy&2QÞK¿:¢SÓÜmò-ù¯$ÿ†ž÷Pd¹œˆÝ=Š‚ûTKJ'|gé«T[ÿ3qçcãócOQ´N>MNËÀWÎ¹ÛBpwïù ™¼=µ^¾tŽ³~.8ášï#Q!¼³KÆaYÈûPúY…J5;áò“ùñ»H¤÷Ø8~'n‡ËÚ¸õ)¨lžÁO°Æ¦?2ÛŒ}éüŠž"h	ÑÑ
Ø*±?Zæ1kÁôý5gãÞ²ûºì>·®òŽƒ¾Ý$êŸË+wuáÎsÏ‰7ü˜{6€ñƒÌµžÀºxÖU£U+ÎÒZ…*cn0Ú>äïñg;ì\V•.!lK‘ëRä"xè¤Xj5L¿Ð¶¶cÜ¢+Þˆ¯®Bm¶5êaÔ"ÈîÚ²ýjnwç}5(;Ç
$¢§®ýðbûo‰5‹=U9k]Â8?Õæx®ôdº¥×­8„¿ò:ï‡xÄœçÞ…×îH|"0 ¤›+UNy÷Ð(T°+º‡åãß-Îy/C”ròÎeï]
ç-tN\„Wâ*ëxZÚQÒhZõÁ±w3³È#u?1VyˆÉ$-—ŒerÛ²¯þ
<«£l6¡ùF¯ùF\
š““Ç”Q¢îþüËó»ÄÍÒ~¯æðópkl$ë±vµ$èð{;ãNs?„‡w‡‰0„Þ<~·ª––£‚´°”ÐÉÕeÆú¶‰æW0c5•3q½bœ–üýw„œrY–(7´Á—OˆIþŒ¸ªMÝ†WèšÆîPz‘´QŒ¿¢'-ÿ,™j<Ú72—­¹¢QÏCÜŽÏÙ„À;ñs,±ÓlÆò:¯)¿¦È?®éðÆÿè4û0×‘žÍËV©´*Ü‚6çV€‹¦ì•N4þÕYì‡{%¸.O-
íe"õIQ î1€ŒRÂ±jƒJ}?Cpík–ãBEÿ#Á—@ß+rwà«eB[™<à´ÓÆuâÕÎ¶þRÃ	çs¹ -Zù… “xÐ““‘/êù}óîýáOßþè[¶æS“÷VýÂ=!¨ã(ï¹êbj0^‹2E9Sÿï{¬jíMTÕÊ55ž¶I¨¦Ä8’"=8±M4;KŠI-†V¼ÆÔŒö¶Õ‰_
ƒ³âàt~pžpÔÇô[ÑÚØ¸|,ŽZÚ?/šG^dyûHÎr ~Öìd©ÿÅy˜ý$o;1¶ÆÚ|Ã‘||sºl<W í©§í¢LÂšÀÃtÈ?LÂ„kS¿Hê[<h¾7tE€G¿°ÎUËíÕ¾Tå6ÕˆqÓåp™Ô9×ðçÅåÁÀw‚PAMÎaF³·?Y•59+zIé\Ÿ‚wMQEL1i³eÇ\lI2n ÛeYPLÇ‹GGÔ‡ÉÅCïÁ½Oã÷€&âK^áÜ6DƒöV¤R!’¯6ÞZÑ“;k]5¥æ­Œ¨í°Ÿ*øŽëžK÷—ø^=Ÿ5¢`‰¯9Xc<õ¡nQq3Š—¸?ˆ'hÕ,Û¡÷'Ø®JSyÉ7å72 I¯‚Â8ßaì§"9ÜžéË"ò½d5NÐuM	Èò,£Ø2‰¹¬™âÔ.nõý­—žù /Ú9cjõ³®«©eè£3˜VØƒ²—#ß‘Ç>š×—OuÕÏ{ÛòmrlMcÐ~ºdå_"=Õp“¦¬©sÝ$<†Ç¬CÏyÞ¾£-¤cø©ìÉé²„S¥*Z4Õ¢í>íò+é|Ù»·wÐ—qªÿD[VÝ«"­ŸgOD†›0 ¾ÇÕð¾éÑÁÿ0£pÀŸ¯½§Òï!Ý›J^è\ ˜!¨§ÔwƒÐA]´”ÜµX¶Ÿ."|Ž7›pã¼£q~"R»¨¿—hJêÎ:Ìó‹¤…en’ç¨Þ^ gH*uEp*z®ÌO#„‹×„?U&²Ú²ô.Xêg|¿waèµòYu\¨³ÕZNtJ- ]k£Ô»Æç˜K±4SÛXP¹LóUCÏ	¯@0Ár³ðë„±ƒ#©RC¾(æû­ÏœÔñcŽtwõbÍa÷"eøI­}‘:S	µ»…åžûºÊ[Ä»º6u÷­1ÕuS¹È……ì¤‡ÇÕSÈ•±n‹ÞõÑ“™_¿uðVðXƒ ÞBw†·š1ÆS¹×0»ëÝ·››ÜmÀÖÃb½¹=În‡djaYâ&fÖö7¬3±-V73¯›BÕa°‰Y™ÇW~<(«êö>Û¨Lµ‘Mmº‘IÝ§«â` •ãmiv¿¥Ù¥À]†È¦8n«´{­Ò6ÀñZuZþ-Š÷S°mfŒŸeŠ[ï¥vÛÐ?Ç·¥ÁÝ–q›ßÅAê-lwZÑmhp×‚m[ÜÝqq·©©m^5lë¼;®ó6¶³u˜EèqÐàã{±çn›OµS¶’‰ZÚç’·Äj_ç¡­$t·t¡üŠ­LLê¦,û¯ÓK±iz§+—‚øE}¿›íêbüëZ§ow_s?óÛm„ç÷ù¢ß}W«)´ã]2všj`µ«5ÕN»¯L_šßEÕo€ªÄµ½Òvµ5¥£H´¶ïmËŸ;åÏ#aÏµ¸ÃyÀ–7wÊÖñFœ¹„)‘¸ëpæB¦œ“ÕvØF4¸’/¦ÚÒåŽébª‡Î–ˆW&j/?®ç÷Á”ö?p_ÚšRÃéAïŒè˜ÞðÛ½ÛÕw™e:ôùWj×Æ€Úº¶1Ú4ÕoÇºÅ÷>ñ•	+ Î‚ö;¾»c„­:KòÆê™@[¸¢ZÒVÛe¾xÎ¨ý…6Z³øž÷¨‹vÔ5aWªHo%O¥ïVåûa§*{`E‘#ñîLûÿŒ ¹ºjØ¨vùb‰ÿ¿zotÃž^á•uþBÌ%7wG¢r<šÓ…T§Ön¿§ó»ƒÅœñËñcE#E9zÙÃßGâÆê7ùŒ¸hëÞ%óÛM{c~~ûÞöì]ŠØà½–­ýC¸ÑÿP&h±k   0707010005c83c000081b40000000000000000000000015ab2175400000eae000000660000000000000000000000000000003c00000000root/usr/share/doc/opensvc/template.service.fs.ntfs.conf.gz   ‹      í]ksÛ6ýž_™LÆÎ¬¤µ³Íç1õn“Ì¶MgÝf?t:6D‚Ö$Á du»ÿ}Ï½ )P~HŽ±»ÒL[$/Á{Î}—ÈÓ§·ùyòTÜâ‡Äev Ü¢R¢t™½¹¸[ü@ÜíêîÉÏ™}º÷Ë“Wü¼oø‰Ÿ<¡QŸªÅÜÔéÁòæI­¤SÇ¦rÚ”§oñCz²‰©ä8WÝ=¬Ejõk£kµÌ{™[>RÕf¦-Æ£ËÉA|Iª2Ùän9üïMÉt9Uµv²LÂ}r%3ñVL•LÛA('Wµ?n+•èL'8g¢J\›Ði‰)gªŽäÛi®ÎXs¸·Mpà0M5©Jæ"èL8#*iù_7U"7àðÌäM¡‚‚!º(d™Ž"êéß”Àw¢”8OÖø“ãäTXåpÞ“W}l äpˆ:=iLcÅâ2Hg“ÿk#°~¬\“14©¶§bR›¦â_3+»°N¸`¦¼M“§b¬"˜ºÇP©ÈjS04³	àÈŒ¹l¿(
ídK¬’tèËÃ!³jU×8GÛ³‘8ŒPbAê¬ª•åpþëòí³ÿ¼ÿç»w¿ÿðéè÷Oÿ/#Ç'^‰¸ð0¡ë,ë^hœ›ä´5R?é62]ÈI805Öa°„fMt.0-LSº‘øFg™ªUéZÉ‰,awäûÌ¦í‘¨Ò``¢±á&tí×ô¹]”N’·~…¯¯†ø7¯¯/‡ñåNòË€L
ñ®’£Åˆ¡|Ši#ñ!#TíPTFãom#pjUIÂ~Ç|0$¹•tSQƒ,„-ïu%<ƒþåÐy`k|ñª³£•0Æê…òÖj—rŠ-ÿÏ©8dZ­IKkÂ|)«ã/ë’2?d«qNBŒ´
N]²Êè±VÃ=‚JÏ›”Òé™ÊËvÿM5ÜìŒþFÏýàš8ædµ¦ðÐ>áÉÅþÞ³6ÑˆÆÆÙ ¬	éyÕ8ïýè†H=â<£=ˆ[ûüé¢äaãLEH”¹ø¢Ÿ ˜5M(»*¬)5úNMäxá”g^Gª«¹Wšcd;ª¨Ü±›úñÙswä¶2NÔR:"ö`9Zñûò>çRÒ±1¸U¹J`ÊAÀ‰‡ëwâc¥Ê£Osç"¨c²š€UõL’ÓÀQD9Ù«ü™¡òÃS‚q¨î@BÙ¤ü‡‰#Cy‰v²ž¨8õÑE•«áUúj05` ÆÑT.õ¶KëàÊRØŠ²gÏGË‡‰íd¤28@¡}g•L;m>&˜!³œ­·Rõ°ò=³ÅXZíÙ»*	$ö \BãjrœTÍCyøLzÖ¦
ôJíRp¨ú«	D‹ŸiÌ¿ôg òÜÌÒfŒŽ…'Æº$”à2Ù‹ù»8RïHüÓ$CìËºS$¸£Áp¢–%Òù±7Ø¼ /ö†/×€à¤/.³PÅÌ»ê5õÂBñdÒwÑ÷<—t]p	·µà‚Çv
ñ[ˆ¯†ø‚`ƒì]MTÝƒÿª.UÞå(3™#biªpU:@®¯“)~ndàëý½_	¯øö²n ÍpŒ,6EÚâ¡JÛÔ!«	KBŽ÷@¬à£põÚÅÉßƒ"¤’“÷^žÓŠE‚ÑˆÊþ Š9Ú$‚ƒ„ ´Á0êv>Xô(ö½Ü_Ç¾_ãä–|›ú, ã)Tª¿ãËHÂ'¡	êP°·.ÈÏ’êL<‹s»t+ ž–¯‘c‹2<!‰’èœÀ8=’µ,”SõH¼‡#Rg’œ‘è[]6g"á+ë÷áõ$Rž¦Ìu¡Ý›s¦XšëQ/¬ŽÅ_Hã(›búáñÔÆ*hjcDâ”„áAoÚ¤#ñrïÙ×8'Èzéëú‚thýXèœ½3"innü¹1ç™ÈDy/ýêˆzlLqLsÿµÉ·ä¿’üzÞC‘år"v÷(
îS-)ðœ¥¯RmýÏÄ#ÌCŒ=E@Ò:ù49-_9çnÁÝ½çdòöÔzùÒ9Îú¹à„k.¼ŽD…ðÎ.w^„e!ïCég]*Õxì<„ËOäÇï"A~ÞcãHø,¸.kãÔ§ ²Ex?Á˜fHüÈl3ö¥ó+zŠ %DD+`«Äþh™OÄ¬Ó÷×pœ{Ëîë²ûÜºÊ;úv“¨.¯ÜÕe„;ÏI<$jÜðc6ìÙ Æ2×zëâY_TV­8?Jh}ªŒ¹Áhû¿ÇŸ5ì°sYUº„°-E®K‘‹à “b©Õ0ýBÛJØŽq‹®x#¾º
µÙÖ¨„Qÿ‰ »kËö«¹ÝCöÕ ì+ˆžºöÃ‹í¿%Ö,öTå¬u	ãüT›ã¹Ò“é–^·âþÊë¼>âsž{^»#ñ‰À< n®T	|8åÝ@£PÁF¬è–|c´8ç½YPÊÉG8—½w)œ·Ð9qZ\‰O¨¬ãiiGI£iÕÇÞÍÌ"ÔüÄXeä!2$“´\2–ÉilË¾ú+ð¬Ž²Ù„æ½Väq)hNN_PF‰ºûó/Ïï7Kø½šÃÏÃe¬±‘8¬ÇÚÕ’ ÃïíŒ;ÍýÞz$ÂzóøÝªRXzXŽ
ÒÂRB'W—lèÛ&j˜w\ÁŒÕTÎ4ÄõŠqZð÷ßrf4ÊeY¢pÜÐ_>!&ù3âr¨6Et^] k»CéEÒF1þŠž´ü³dªñhßtÈ\¶æŠ
D=>q;>?fïÄÏ±tÆN³Ëë¼¦üš"ÿ¸¦/Àÿ£ÓìÃ\G<Dz6/[¥ÒªpRpØœ[.š²T:ÑøWg±î•àº<µ(´—=ŠÔ'E¸Ç 2vJ	Çª*õýÁµ¯YŽýŒ_}¯ÈUÜ¬–	An=dòT€ÓNGÔ‰Ww>8ÛúK'œÏå‚Z´hå‚NNàAONF¾¨Cæ÷Í»÷‡?}û£oÙšOMÞ[õ÷„ vŒ£x¼çª‹©ÁxEH,Ê4åLü¿ïm°ªýµ7QU+×ÔxÚ&¡šã<HŠôàÄ6iÐì,)&µZñ?R3ÚÛvT'~).ÌŠƒÓùÁIxÂQ{OÐoEkcãò±8jiÿH¼hy9åí#9ËøY³“¥Vüçaö“¼íÄØkóGòñÍé²Qð\´§ž¶‹2	kÓ!ÿ`0	®Mý"©oñ ùÞÐþÂ:W-·WûR•ÛTklx ÆM—ÃeRç\ÃŸ—ß	B59‡ÍÞþdUÖä¬è%¥s}
Þ5E52Å¤Í–s±%È¸l—e]@1/Q&½÷>5Žßšˆ/94x…sÛÚ;X‘J…H¾ÚxkADOî¬uÕ”š·2¢´Ã~ªà;®{.Ý_â{õ|Öˆ‚%n¼ä`ñÔ„ºEÅÍ|(^âþ ž4¢UC°l‡ÞŸ`»*M9ä%ß”ßÈ€&½

ã|‡±Ÿ6Šäp{¦/‹È÷’qdÔ8A×5% Ë4²ŒbËh$æ²fjˆoP»¸ÕGô·^Txæƒ0¼xhçŒ©ÕÏº®¦–¡Î`ZaÊ^Ž|Gûh^_>ÕU?ïnË·EÈ±5eŒAûé’•ŠôTÃMvš²¤Îu“ð,³=wäyûŽ¶ŽMà§²'§ËN•ªhmÐT‹¶û´Ë¯¤óeïrÜÞA_Æ©þmYu?¬Š´~ž=nÂ€øWsÀû¦GÿÃŒÂ¾öœJ[¼‡to*mx¡s`† žRßBuÑRrwÔbÙ~ºˆ4ð9ÞlÂóŽÆqtø}ˆHí¢Jü^¢)©;ë0Ï/’–	¸Iž£z?þyPœ!©ÔÁ©è¹v0?.^þT™ÈjËÒ»`©Ÿ=ðýÞ„¡×ÊgiÔq¡ÎVkuj8Ñ)µ€t­YŒRïŸc.ÅÒLmcAå2Í{T='¼ÁËÍÂ¯ÆŽ¤J}ø¢˜ï·>sRÇ9ÒÝÕ‹5‡Ý‹”á'µö}DêL%Ôî–wzîë*o5ïêÚÔÝ{´ÆT×Må"²“WO!WÆº-z×GOf~yüÖÁ[Ácz€zÝ-ÞjÆOå^Ãì®ct[Ünnr·[‹õæö8»©…e‰›˜YÛß°ÎÄ¶XÝÌ¼n
U‡Á&fe_ùñ ¬ª[Øûl£2ÕF6µêF&u#œZ®Š7€VŽ·¥Ùý–f—w"›â¸­ÒîµJÛ ÇkÕi=ø·(ÞOÁ¶™1~–)n!¼—ÚmC3ü#Ü–w[Æmf|}¨·°ÝiE·¡Á]¶mqwÇÅÝ¦¦¶yÕ°­óî¸ÎÛØÎÖae ÇAƒïÅž»Al>ÕNÙJ&jhiŸKÞ«}‡¶’ÐÝfÐ…ò+¶2I0©›²ì¿N/ÅN¤é®\8\
âõýn¶«‹ñ¯k¾Ý}ÍýÌo´žßç‹~÷]­¦ÐŽwÉØiªuÔ®ÖT;í¾2}i~U¿ n¨×öJÛÕÖ”Ž ÑÚ¾·-î”?„=×âç[ÞÜ)oXÇqæ¦Dâ®Ã™™rNVÛaÑàJ¾˜jK—;¦‹©:["\˜¨½ü¸žßS:ØÿÀ}ik6Jo¤½3¢czÀo÷nWße–éÐo ä_}¨]?jëÚÆhÓT¿ëßûÄW&¬ 8ÚïxøRì2Œ¶ê,É«gj mMàŠjI[m—ùâ9£öÚhÍâ{Þ£.ÚQ×„]©"Y¼•<•J¼[•ï‡ª<ìuDŽÄ»3íÿ3‚Häê¨a£Úå‹%þÿ:è½Ñ5{z…WÖù1—ÜÜ‰ÊñhN2PZ»ý6œÎïsrÄ/ÇŒåèe<‰<¨ßä3â¢­{—Ìo7í}øùí{/Ø³w)bƒ÷Z¶nôáFÿèá4°k    0707010005c83d000081b40000000000000000000000015ab2175400000eae000000660000000000000000000000000000003c00000000root/usr/share/doc/opensvc/template.service.fs.ocfs.conf.gz   ‹      í]ksÛ6ýž_™LÆÎ¬¤µ³Íç1õn“Ì¶MgÝf?t:6D‚Ö$Á du»ÿ}Ï½ )P~HŽ±»ÒL[$/Á{Î}—ÈÓ§·ùyòTÜâ‡Äev Ü¢RÂ$™½¹¸[ü@ÜíêîÉÏ™}º÷Ë“Wü¼oø‰Ÿ<¡QŸªÅÜÔéÁòæI­¤SÇ¦rÚ”§oñCz²‰©ä8WÝ=¬Ejõk£kµÌ{™[>RÕf¦-Æ£ËÉA|Iª2Ùän9üïMÉt9Uµv²LÂ}r%3ñVL•LÛA ù:Uµ?n+•èL'8g¢J\›Ði‰)gªŽäÛi®ÎXs¸·Mpà0M5©Jæ"èL8#*iù_7U"7àðÌäM¡‚‚!º(d™Ž"êéß”Àw¢”8OÖø“ãäTXåpÞ“W}l äpˆ:=iLcÅâ2Hg“ÿk#°~¬\“14©¶§bR›¦â_3+»°N¸`¦¼M“§b¬"˜ºÇP©ÈjS04³	àÈŒ¹l¿(
ídK¬’tèËÃ!³jU×8GÛ³‘8ŒPbAê¬ª•åpþëòí³ÿ¼ÿç»w¿ÿðéè÷Oÿ/#Ç'^‰¸ð0¡ë,ë^hœ›ä´5R?é62]ÈI805Öa°„fMt.0-LSº‘øFg™ªUéZÉ‰,awäûÌ¦í‘¨Ò``¢±á&tí×ô¹]”N’·~…¯¯†ø7¯¯/‡ñåNòË€L
ñ®’£Åˆ¡|Ši#ñ!#TíPTFãom#pjUIÂ~Ç|0$¹•tSQã2ËÈ÷ºž‚Aÿrè<0Œ5>‡xÕÙÑJcõBykµK9Å–ÿçT2­V¤¥5a¾”ÕñõI™²Õ8'!ÆFZ§.YeôX«áA¥çMJéôLå‹å»ÿ¦nvFF£çþFpMs²ZSxèGŸðäbïY›hDcãl Ö„ô¼jœ÷~tC$‚ŽŽˆqžÑÄ­}~ŽtÑrˆ0„q¦¢F$J†\|ÑO Ìš¦N”]Ö”š}§&r¼pÊ3¯#ÕÕÜ+Í1²UTîXŽMýøì¹;r['j){°­ø}yŸs)éØÜª\%0å àÄŽÃõ;ñ±RåÑ§¿‰¹ÎsTŽ1YMÀªz&Éià(¢œìÕþÌPùá)Á8Tw ¡ìÒþCŽÄ‘¡<†D;YOTœúè¢ÊUð*}5˜0 ãhª
—z[È¥upe)lEÙ³ç£åÃD‚v2ÒÇ Ð¾³J¦6“	ÌYÎÖ[©zØùžÙb,­öì]•{ .¡q59Nªæ¡Î<|¦½kSz¥v)8TýÕ¢ÅÏ4æ_ú3ynæ‚ˆGi3FÇÂ“c]Jp™ìÅüÝ ©w$þ†¿i’!öeÝ)ÜÑà8QËéüØì^Ð{Ã„kÀ	pÒ—ƒY¨bæÝ	õšzá¡x2é»è{žKº.¸„ÛZpÁ€c;…ø-ÄWC|A°Aö®&ªîÁÿU—*ïr”™Ì±4U¸* ××É?72ðõþÞ‹¯„×?|{Ù7€f8F›"mq‰P¥mêÕŽ¥!Ç{ VðQ¸zíâä‹ïA‘
RÉÉ{/ÏéEˆ"A‚hDeP	EˆŽíGÁABPÚ`u;,z”û^î¯cß¯qrK¾Mý‚ñƒ*Õß‰ñe$á“Ðu(Ø[ägIu&žÅ9ˆ]ºOË×È±Ež¿DItN`œÉZÊ©z$ÞÃ©3I	Î€Hô­.›3‘ðÈõ‡ûðz)OSæºÐŽîÍ9S,Í€õ¨VÇâ/¤q”M1ýðø…Njc4µ±
"qJÂð 7mÒ‘x¹÷ìkœd½ôu}A:´~,tÎ‹Þ‘477þÜ˜óLd¢¼—~uD=6¦8¦¹ÿÚä[ò_Iþ=ï¡Èr9»{÷©–”NøÎÒW©¶þgâÎÇÆæ!Æž¢ i|šœ–¯œs·…àîÞó2y{j½|égý\pÂ5ÞG¢Bxg—Œ;/Â²÷¡ô³.
•j<vÂå'òãw‘ ?Hï±q$üNÜ—µqêSPÙ"<ƒŸ`	L3$~d¶ûÒù=EÐ¢¢°Ub´Ì'bÖ‚éûk8ÎÆ½e÷uÙ}n]å}»IÔ?—Wîê2Âç$ž5nø1öl ã™k=uñ¬/ªF«Vœ¥´>
UÆÜ`´}ÈßãÏvØ¹¬*]BØ–"×¥ÈEðÐI±Ôj˜~¡m%lÇ¸EW¼_]…ÚlkÔÂ¨ÿDÝµeûÕÜîÎ!ûjPvŽHDO]ûáÅößk{ªrÖº„q~ªÍñ\éÉtK¯[qåuÞñˆ9Ï½¯Ý‘øD` H7Wª>œòî Q¨`#VtËÇ¾1Zœó^†,(åä#œËÞ»Î[èœ¸-®Ä'TÖñ´´£¤Ñ´êƒcïff‘Gê~b¬2ò’IZ.Ëä4¶e_ýxVGÙlBóŒ^+ò¸4''/(£DÝýù—çw‰›¥	ü^Íáçá2ÖØHÖcíjIÐá÷vÆæ~ï=a½yünU),=,Gia)¡“«Ë6Œôm5Ì;®`Æj*gâzÅ8-øûï93å²,Q8nhƒ/Ÿ“üq9T›"º¯.Ð5Ý¡ô"i£EOZþY2Õx´o:d.[sE¢Ÿ‡¸Ÿ³	wâçX:c§ÙŒåu^S~M‘\ÓàÿÑiöa®#"=›—­RiU¸)8lÎ­ MÙ*hü«³Ø÷Jp]žZÚËEê“¢@Üc ;¥„cÕ•ú~†àÚ×,Ç…ŠþF‚/¾Wä*îÀVË„ ·2y*Ài§#êÄ«;œmý¥†ÎçrA-Z´òA''ð ''#_Ô!óûæÝûÃŸ¾ýÑ·lÍ§&ï­ú…{BP;ÆQ<ÞsÕÅÔ`¼"$e‹r¦
þß÷6XÕþÚ›¨ª•kj<m“PM‰q$Ezpb›4hv–“Z­x©ím;ª¿fÅÁéüà$<á¨=Ž'è·¢µ±qùXµ´$^4¼Èòö‘œå@ü¬ÙÉR+þ‹ó0ûIÞvblµù†#ùøætÙ(x®@ÚSOÛE™„5‡é0˜„	×¦~‘Ô·xÐ|oèŠ a«–Û«}©Êmª56<ã¦Ëá2©s®áÏ‹Ë‚ï¡‚šœÃŒfo²*krVô’Ò¹>ïš¢Š™bÒfËŽ¹Ø’dÜ ¶Ë². ˜ŽŽ¨“‹‡Þƒ{ŸÇïMÄ—¼Â¹mˆí¬H¥B$_m¼µ ¢'wÖºjJÍ[QÚa?Uð×=—î/ñ½z>kDÁ7^r°ÆxêBÝ¢âf>/qOÑª!X¶CïO°]•¦ò’oÊod@“^…q¾ÃØOEr¸=Ó—Eä{É82jœ ëšåYF±e4sY35Ä7¨]Üê#ú[/*<óA^<´sÆÔêg]WSËÐGg0­°e/G¾#}4¯/ŸêªŸ÷·åÛ"äØš2Æ ýtÉÊ¿Ezªá&;MYRçºIxY‡ž;ò¼}G[HÇ&ðSÙ“Óe	§JU´6hªEÛ}ÚåWÒù²w9nï /ãTÿ‰¶¬ºVEZ?Ïžˆ7a@|«9à}Ó£ƒÿaFá€?_{N¥-ÞCº7•6¼Ð¹@0CPO©ï¡ƒºh)¹;j±l?]Døo6áÆyGã8:ü>D¤vQ%~/Ñ”Ôu˜çIËÜ$ÏQ½ÿ¼@(ÎTêŠàTô\;˜ŸF¯	ªLdµeé]°ÔÏø~ïÂÐkå³4ê¸Pg«µ:5œè”Z@ºÖ,F©wÏ1—bi¦¶± r™æ=ª†ž^`‚åfá×	cGR¥†>|QÌ÷[Ÿ9©ãÇéîêÅšÃîEÊð“Zû>"u¦jwË;=÷u•·‰wumêî=Zcªë¦r‘ÙI«§+cÝ½ë£'3¿<~ëà­à±=@½…îo5cŒ§r¯av×1º-n77¹Û€­‡Åzs{œÝÈÔÂ²ÄMÌ¬íoXgb[¬nf^7…ªÃ`³2¯üxPVÕ-ì}¶Q™j#›Úu#“ºN-WÅÀ@+ÇÛÒì~K³K»‘MqÜVi÷Z¥m€ãµê´ü[ï§`ÛÌ?Ë·ÞKí¶¡~ŽnKƒ»-ã63¾Šƒ>Ô[Øî´¢ÛÐà®Û¶¸»ãânSSÛ¼jØÖyw\çmlgë0‹2Ðã ÁÇ÷bÏÝ 6Ÿj§l%5´´Ï%o‰Õ¾ÎC[Ièn3èBù[™$
˜ÔMYö_§—b'ÒôNW..ñ‹ú~7ÛÕÅø×µNßî¾æ~æ·ÚÏïóE¿û®VShÇ»dì4ÕÀ:jWkªv_™¾4¿‹ªß 7T‰k{¥íjkJG	hmßÛ–?wÊŸGÂžkq‡ó€-oî”7¬ã8s	S"q×áÌ…L9'«í°hp%_Lµ¥ËÓÅT-	®LÔ^~\Ïïƒ)ìà¾´5¥†7ÒƒÞÑ1½à·{·«ï2Ëtè7ò¯>Ô®µumc´iªßŽu‹ï}â+V œíw<|)vÆ[u–äÕ35€¶&pEµ¤­¶Ë|ñœQûm´fñ=ïQí¨kÂ®T‘,ÞJžJ%Þ­Ê÷ÃNUöÀŠ:"GâÝ™öÿA$ruÔ°QíòÅÿôÞè†=½Â+ëü…˜KnîŽDåx4§¨N­Ý~Nçw‹99â—ãÇŠFŠrô²†¿ÄÔoòqÑÖ½Kæ·›öÆ>üüö½ìÙ»±Á{-[7ú‡p£ÿ'¥§ô°k    0707010005c83e000081b40000000000000000000000015ab2175400000eaf000000660000000000000000000000000000003d00000000root/usr/share/doc/opensvc/template.service.fs.ocfs2.conf.gz  ‹      í]ksÛ6ýž_™LÆÎ¬¤µ³Íç1õn“Ì¶MgÝf?t:6D‚Ö$Á du»ÿ}Ï½ )P~HŽ±»ÒL[$/Á{Î}—ÈÓ§·ùyòTÜâ‡Äev Ü¢RÂ$™}qcq·ø¸ÛÕÝ“Ÿ3ûtï—'¯øyßø'~ò„†}ªsS§Ë»'µ’N›ÊiSZœ2¼Å)Ê&¦’ã\u÷ü±n¨Õ¯®Õr0ïenùHU›™¶.'ñ%©Êd“»åð¿7%ÐåTÕÚÉ2	÷É•ÌÄ[1U2mèëTÕþ¸­T¢3àœ‰*qmB§%¦œ©:’o§¹:cÍáÞ6ÁÃ4Õ¤*™‹ 3áŒ¨¤åÝT‰ÜLt‚Ã3“7…

†è¢e:Š¸s¤Sß‰Râ<YãOŽ“Sa•ÃyO^õ±’Ã!Fèô¤1‹Ë Mþ¯aŒÀúp°rMÆÐ¤ÚžŠImšŠÍt®ìÂ:Uà‚™Nð6MžŠ±Š`êC¥"«MÁÐÌ&€#3æ2,°ý¢(´-±JVÐ¡/[tV‡Ìª<T]ãm#ÌFâ0B‰©³ªV–/Àù¯Ë·ÏþóþŸïÞýþÃ§£ß?ýý¿ŒŸx%vàÂÃ„®³¬{5 qn’ÓÖ@Hý¤ÛÈtt!'áÀÔX‡Áš4Ñ¹À´0MéFâeªV¥k%'²„Ý‘ï0O ˜¶gD¢Jƒ‰Æ†›Ðµ_ÓwlävQ:IÞú¾¾âß¼¾¾Æ—;É/2)Ä»JvŒ#†ò)¦Ä‡ŒP´G@Q¿µÀ©U%	/øIóÁäVÒMEmŒË,[ ßëJx
ýË¡óÀ,0ÖøâUgG+aŒÕå­Õ.å[þŸSqÈ´Z5’–Ö„ùRVÇ_<Ö?$e~ÈVã4œ„7iœºd•Ñc­†{•ž7)¥Ó3•/–ìþ›Š¸Ùý=žûÁ5qÌ1È6jMá¡}Â“‹ý½gm¢³XÒóªqÞûÑ‘:>8"zÄyF{·öù9ÒE?È!ÂÆ™Š‘(rõE?0kš:QvUXSjôšÈñÂ)Ï¼ŽTWs¯4ÇÈvTQ¹c96õã³çîÈmeœ¨¥t
DìÁr´â÷å}Î¥¤ccp«r•À”ƒ€;×ïÄÇJ•GŸþ&æ:ÏEP9Æd5«ê™$§£ˆr²W7ø3Cå‡§ãPÝ„²;Hø!8G†òíd=Qqê£‹*WÂ«ôÕ`jÀ Œ£©*\êm!—ÖÁ•¥°eÏž–	ÚÉH;dp,€BûÎ*™vÚ|L&0Cf9[o¥êa/ä{f‹±´Ú³wUHì¸„ÆÕä8©š‡:óð™ô¬Mè•Ú¥àPõWˆ?Ó˜éÏ@ä¹™"¥Í1O>ŒuI(Áe²ówp¤Þ‘øþ¦I†Ø—u§HpGƒ;àD-K¤óbo°?xA_ì_®'ÀI_\f¡Š-˜w&Ôkê…/„âÉ¤ï¢ïy.éºànkÁŽíâ·_ñÁÙ»š¨ºÿ?T]ª¼ËQf2GÄÒTáªt€\_'SüÜÈ<À×û{/¾^ÿðíe/Ü šáYlŠ´Å5$B•¶©CV8–„?îXÁGáêµ‹“/¾E*H%'ï½<§/0 Š	¢•ýA%!:r´I	AiƒaÔí8|°èQ
ì{¹¿Ž}¿6ÆÉ-ù6õ/X@
ÆR¨T'Æ—‘„OBÔ¡`o]Ÿ%Õ™xç véV@.<-_#Ç9<dxþB%Ð9qz$kY(§ê‘xG¤Î$%8"Ñ·ºlÎDÂW ×îÃëI¤<M™ëB;º7çL±4Ö£^X‹¿ÆQ6ÅôÃã:©U0ÐÔÆ*ˆÄ)	ÃƒÞ´IGâåÞ³¯qNõÒ×ôéÐú±Ð9/zgDÒÜÜøscÎ3‘‰ò^úÕõØ˜â˜æþk“oÉ%ù7ô¼‡"ËåDìîQÜ§ZR:á8K_¥ÚúŸ‰;G˜‡{Š:€¤uòirZ¾rÎÝ‚»{ÏÈäí©õò¥sœõsÁ	×\x‰
á]2î¼ËBÞ‡ÒÏº(TªñØy—Ÿ<ÈßE‚ü ½ÇÆ‘ð;Yp;8\ÖÆ¨OAe‹ð~‚5&0Íø‘ÙfìKçWôAKˆˆVÀV‰ýÑ2ŸˆY¦ï¯á8÷–Ý×e÷¹u•wôí&Qÿ\^¹«Ëwž“xHÔ¸áÇlØ³Œd®õÖÅ³¾¨­Zq~”>Ðú(TsƒÑö!?kØaç²ªt	a[Š\—"9À#@'ÅR«!`ú…¶•°ã]ñF|uj³­Q?£þAv×–íWs»;‡ì«AÙ9V =uí‡ÛK¬Yì¨ÊYëÆù©6Çs¥'Ó-½nÅ!ü•×y?|Ä#æ<÷.¼vGây  Ý\©øpÊ»€F¡‚XÑ=,øÆhqÎ{² ”“p.{ïR8o¡sâ"´¸ŸPYÇÓÒŽ’FÓªŽ½›™E©ø‰±ÊÈCdH&i¹d,“ÓØ–}õWàYe³	Í0z­È7âRÐœœ<¾ Œu÷ç_žß%n–&ð{5‡Ÿ‡ËXc#qXµ«%A‡ßÛwšû!<¼;ôH„!ôæñ»U¥°ô°¤…¥„N®.3Ø0Ð·MÔ0ï¸‚«©œiˆëã´,àï¿#äÌh”Ë²D	à¸¡¾|BLògÄåPmŠè6¼º@×4v‡Ò‹¤bü=iùgÉTãÑ¾é¹lÍˆz|âv|~Ì&Þ‰ŸcéŒf3–×yMù5EþqM_€7þG§Ù‡¹Žxˆôl^¶J¥Uá¤à°9·\4e¨t¢ñ¯Îb?Ü+ÁuyjQh/{©OŠqdì”ŽUTêû‚k_³*ú?	¾ú^‘«¸X-‚ÜzÈä¨ §6Ž¨¯î|p¶õ—N8ŸËµhÑÊ/œÀƒžœŒ|Q‡Ìï›wïúöGß²5Ÿš¼·êî	AíGñxÏUSƒñŠX”i,Ê™*øßÛ`Uûko¢ªV®©ñ´MB5%ÆyéÁ‰mÒ8 ÙYRLj1´â5~¤f´·í¨NüR\˜§óƒ“ð„£ö8ž ßŠÖÆÆåcqÔÒþ‘8xÑ<òr ËÛGr–ñ³f'K­ø/ÎÃì'yÛ‰±5ÖæŽäã›Óe£à¹iO=meÖ¦CþÁ `&\›úERßâAó½¡+<
ü…u®Zn¯ö¥*·©ÖØð@Œ›.‡Ë¤Î¹†?/.7¾„
jr3š½ýÉª¬ÉYÑKJçú¼kŠ*jdŠI›-38æbK2qØ.Ëº€b:^<:¢>L.zî}j¿4_rhð
ç¶!´w°"•
‘|µñÖ‚ˆžÜYëª)5oeDh‡ýTÁw\÷\º¿Ä÷êù¬KÜxÈÁã©u‹Š›ùP¼ÄýA<iD«†`Ù½?ÁvUšrÈK¾)¿‘MzÆùc?mÉáöL_‘ï%ãÈ¨q‚®kJ@–/hdÅ–ÑHÌeÍÔ8ß vq«èo½4¨ðÌaxñÐÎS«Ÿu]M-CÁ´Â”½ùŽ<öÑ¼¾|ª«~ÞÜ–o‹ckÊƒöÓ%+ÿ
é©†›ì4eHë&áY0<fzîÈóöm!›ÀOeON—%œ*UÑÚ ©m÷i—_IçËÞå¸½ƒ¾ŒSý'Ú²ê~Xiý<{"2Ü„ñ=®æ€÷Mþ‡…þ|í-8•¶xéÞTÚðBçÁA=¥¾„ê¢¥äî¨Å²ýtiàs¼Ù„çãèðû‘ÚE•ø½DSRwÖaž_$-,p“<Gõ~üó¡8CR©+‚SÑsí`~!\¼&ü©2‘Õ–¥wÁR?{àû½;C¯•ÏÒ¨ãB­ÖêÔp¢SjéZ³¥Þ5>Ç\Š¥™ÚÆ‚Êeš÷¨zNx‚	–›…_'ŒI•úðE1ßo}æ¤Žs¤»«k»	(ÃOjíûˆÔ™J¨Ý-,ïôÜ×UÞj$ÞÕµ©»÷h©®›ÊE.,d'=<®žB®Œu[ô®žÌüòø­ƒ·‚Çô õº[0¼ÕŒ1žÊ½†Ù]Çè¶¸ÝÜän¶ëÍíqv;< SË71³¶¿a‰m±º™yÝªƒMÌÊ<¾òãAYU·°÷ÙFeªljÔLêF8µ\o ­oK³û-Í.î2D6Åq[¥Ýk•¶Ž×ªÓzðoQ¼Ÿ‚m3cü,SÜBx/µÛ†fø9F¸-î¶ŒÛÌø6(úPoa»ÓŠnCƒ»lÛâîŽ‹»MMmóªa[çÝq·±­Ã,Ê@ƒß‹=wƒØ|ª²•LÔÐÒ>—¼%Vû:m%¡»Í åWle’(`R7eÙ^ŠHÓ;]¹p¸Ä/êûÝlWã_×:}»ûšû™ßh#<¿Ïýî»ZM¡ï’±ÓTë¨]­©vÚ}eúÒü.ª~ ÜP%®í•¶«­)%@¢µ}o[þÜ)	{®ÅÎ¶¼¹SÞ°Ž7âÌ%L‰Ä]‡32åœ¬¶Ã6¢Á•|1Õ–.wLS=t¶D$¸:0Q{ùq=¿¦t°ÿûÒÖl”ÞHzgDÇô6€ßîÝ®¾Ë,Ó¡ß@È¿úP»6~ÔÖµÑ¦©~;Ö-¾÷‰¯LXp´ßñð¥Øe#lÕY’7VÏÔ ÚšÀÕ’¶Ú.óÅsFí/´ÑšÅ÷¼G]´£®	»RE²x+y*•x·*ß;UyØ+êˆ‰wgÚÿg‘ÈÕPÃFµËKüÿuÐ{£köô
¯¬ób.¹¹;•ãÑœ.d :µvûm8ß5,æäˆ_Ž+)ÊÑË>xþ>76xP¿ÉgÄE[÷.™ßnÚûðóÛ÷^°gïRÄïµlÝèÂþ›Fât±k   0707010005c83f000081b40000000000000000000000015ab2175400000eaf000000660000000000000000000000000000003c00000000root/usr/share/doc/opensvc/template.service.fs.qnx4.conf.gz   ‹      í]ksÛ6ýž_™LÆÎ¬¤µ³Íç1õn“Ì¶MgÝf?t:6D‚Ö$Á du»ÿ}Ï½ )P~HŽ±»ÒL[$/Á{Î}—ÈÓ§·ùyòTÜâ‡Äev Ü¢Râ×òì«›‹»ÅÄÝ®îžüœÙ§{¿<yÅÏû†ŸøÉõ©ZÌM,ožÔJ:ul*§MiqÊð?¤'›˜JŽsÕÝóÇºQt V¿6ºVËÁ¼—¹å#UmfÚb<ºœÄ—¤*“Mî–ÃÿÞ”|@—SUk'Ë$Ü'W2oÅTÉ´„xrUûã¶R‰Ît‚s&ªÄµ	–˜r¦êH¾æêŒ5‡{ÛÓT“ªd.‚Î„3¢’–ÿuS%r3Ñ	ÏLÞ*(¢‹B–é(¢Î‘þM	|'J‰ód?9NN…Uç=yÕÇ@‡¡Ó“Æ4V,.ƒt6ù¿†1ëGÀÁÊ5C“j{*&µi*þ5Ó¹²ëTf:QÀÛ4y*Æ*‚©{•Š¬6C3› ŽÌ˜Ë0°Àö‹¢Ðx@¶Ä*YA‡¾lÑY12«vðPus´0‰Ã%¤ÎªZY¾ ç¿.ß>ûÏû¾{÷ûŸŽ~ÿô÷ÿ2r|â•ØºÎ²îÕ€Æ¹IN[!õ“n#ÓÑ…œ„ScKhFÐDçÓÂ4¥‰ot–©Z•®•œÈvG¾OÀ<`Úž‰*&nB×~Mß±‘ÛEé$yëWøújˆóúúr_î$¿È¤ï*Ù1ZŒÊ§˜62BeÐa Ee4þÖ6§V•$¼à'qÌC’[I7µ1.³l|¯+á)ô/‡Î³ÀXãsˆW­„1V/”·V»”SlùNÅ!ÓjÕHZZæKYñXÿ”ù![ÓpbÜh¤Upê’UFµîTzÞ¤”NÏT¾X^°ûoªáfgô÷h4zîo×Ä1Ç Û¨5…‡~ô	O.ö÷žµ‰F46Î`MHÏ«ÆyïG7D"èøàˆèçíAÜÚççHý ‡Cg*jD¢dÈÅýÀ¬iêDÙUaM©YÐwj"Ç§<ó:R]Í½Ò#ÛQEåŽåØÔÏž»#·•q¢–Ò)±ËÑŠß—÷9—’ŽÁ­ÊUSNì8\¿3+U}ú›˜ë<Aå“Õ¬ªg’œŽ"ÊÉ^ÝàÏ•žŒCuÊî à?„àHÊcH´“õDÅ©.ª\¯ÒWƒ©0Ž¦ªp©·…\ZW–ÂV”={>Z>L$h'#}ìÁ± 
í;«dÚió1™À™ål½•ª‡½ï™-ÆÒjÏÞUI ±àW“ã¤jêÌÃg:Ðk°6U Wj—‚CÕ_M ZüLcþ¥?‘çf.ˆx”6cÄp,<ù0Ö%¡—É^ÌßÀ‘zGâoø›&b_Ö"Áî€µ,‘Îˆ½Áþà}±7|A¸œ '}q9˜…*¶`Þ˜P¯©¾Š'“¾‹¾ç¹¤ë‚K¸­8¶SˆßB|5Ädïj¢êüÿPu©ò.G™ÉKS…«Òr}Lñs#sð _ïï½øJxýÃ·—½ph†cd±)Ò×pUÚ¦YMàXj@rü¸b…«×.N¾ø© •œ¼÷òœ¾PDÀ€($ˆFTö•P„èÈÑ~$$¥†Q·ãðÁ¢G)°ïåþ:öýÚ'·äÛÔ¿`)?HA Rý_F>	MP‡‚½uA~–TgâYœƒØ¥[¹ð´|[äðáùI”t`@çÆé‘¬e¡œªGâ=‘:“”àˆDßê²9	_\¸¯'‘ò4e®íèÞœ3ÅÒXzau,þBGÙcÐ_è¤6VÁ@S« §$zÓ&‰—{Ï¾Æ9AÖK_GÐ¤CëÇBç¼èIssãÏ9ÏD&Ê{éWGÔccŠcšû¯M¾%ÿ•äßÐóŠ,—±»GQpŸjIé„/à,}•jë&î|labì)ê ’ÖÉ§ÉiøÊ9w[îî= “·§ÖË—ÎqÖÏ'\sá=p$*„wvÉ¸ó",yJ?ë¢P©Æcça \~ò ?~	òƒôGÂïdÁíàpYw >•-Â3ø	Ö˜À4CâGf›±/_ÑS-!z Z[%öGË|"f-˜¾¿†ãlÜ[v_—ÝçÖUÞqÐ·›Dýsyå®.#ÜyNây Qã†³aÏ0~¹ÖXÏú¢j´jÅùQú@ë£PeÌFÛ‡ü=þ¬a‡ËªÒ%„m)r]Š\ä  K­†€éÚVÂvŒ[tÅñÕU¨Í¶Fý ŒúOÙ][¶_Ííî²¯eçXDôÔµ^lÿ-±f±G *g­Kç§ÚÏ•žL·ôº‡ðW^çýð˜óÜ»ðÚ‰Oæ€ts¥JàÃ)ï> …
6bE÷°|<à£Å9ïeÈ‚RN>Â¹ì½Ká¼…Î‰‹ÐâJ|BeOK;JM«>8önfy¤.à'Æ*#‘!™¤å’±LNc[öÕ_gu”Í&4Àèµ"ßˆKAsròø‚2JÔÝŸy~—¸YšÀïÕ~.cÄa=Ö®–~ogÜiî‡ððîÐ#†Ð›ÇïV•ÂÒÃrT–:¹ºÌ`Ãx@ß6QÃ¼ã
f¬¦r¦!®WŒÓ²€¿ÿŽ3£Q.Ë%€ã†6øò	1ÉŸ—Cµ)¢Ûðê]ÓØJ/’6ŠñWô¤åŸ%SGû¦Cæ²5WT êñyˆÛñù1›x'~Ž¥3všÍX^ç5å×ùÇ5}Þøfæ:â!Ò³yÙ*•V…[‚ÃæÜ
pÑ”= Ò‰Æ¿:‹ýp¯×å©E¡½ìQ¤>)
Ä=±SJ8Vm0P©ïg®}Ír\¨èÿ`$øè{E®â\`µLrë!“g œvÚ8¢N¼ºóÁÙÖ_j8á|.Ô¢E+¿trzr2òE2¿oÞ½?üéÛ}ËÖ|jòÞª_¸'µcÅã=W]LÆ+BbQ¦±(gªàÿ}oƒUí¯½‰ªZ¹¦ÆÓ6	Õ”çAR¤'¶Iã€fgI1©ÅÐŠ×ø‘šÑÞ¶£:ñKapaVœÎNÂŽÚãx‚~+Z—ÅQKûGâàEóÈË,oÉYÄÏš,µâ¿8³Ÿäm'ÆÖX›o8’oN—‚ç
¤=õ´]”IXx˜ùƒ€I˜pmêI}‹Í÷†®ð(ðÖ¹j¹½Ú—ªÜ¦ZcÃ1nº.“:çþ¼¸Ü øN*¨É9Ìhöö'«²&gE/)ëSð®)ª¨‘)&m¶Ìà˜‹-É@Æ`»,ëŠéxñèˆú0¹xè=¸÷©qüÐD|É¡Á+œÛ†hÐÞÁŠT*DòÕÆ["zrg­«¦Ô¼•5 öSßqÝséþß«ç³F,qãu kŒ§~ Ô-*næCñ÷ñ¤­‚e;ôþÛUiÊ!/ù¦üF4éUPç;Œý´Q$‡Û3}YD¾—Œ#£Æ	º®)Y¾ ‘e[F#1—5Sã@|ƒÚÅ­>¢¿õÒ Â3„áÅC;gL­~Öu5µ}tÓ
{Pörä;òØGóúò©®úy/p[¾-BŽ­)cÚO—¬ü+P¤§n²Ó”5 u®›„gÁð˜uè¹#ÏÛw´…tl?•=9]–pªTEkƒ¦Z´Ý§]~%/{—ãöú2NõŸhËªûaU¤õóì‰ÈpÄ÷¸šÞ7=:øføóµ·àTÚâ=¤{SiÃ3õ”ún:¨‹–’»£ËöÓE¤Ïñfnœw4Ž£ÃïCDjUâ÷MIÝY‡y~‘´°LÀMòÕûñÏ„âI¥®NEÏµƒùi„pñšð§ÊDV[–ÞKýìï÷î ½V>K£Žu¶Z«SÃ‰N©¤kÍb”z×øs)–fj*—iÞ£jè9á&Xn~0vp$UjèÃÅ|¿õ™“:~Ì‘î®^¬9ì^$ ?©µï#Rg*¡v·°¼Ós_Wy«‘xW×¦îÞ# 5¦ºn*¹°ôð¸z
¹2ÖmÑ»>z2óËã·Þ
kÐÔ[ènÁðV3Æx*÷fw£Ûâvs“»ØzX¬7·ÇÙíð€L-,KÜÄÌÚþ†u&¶ÅêfæuS¨:61+óøÊeUÝÂÞg•©6²©-P72©áÔ"p5P¼´r¼-Íî·4»¸ËÙÇm•v¯UÚ8^«NëÁ¿Eñ~
¶ÍŒñ³Lqá½Ônšáçá¶4¸Û2n3ãÛ 8èC½…íN+ºîZ°m‹»;.î65µÍ«†mwÇuÞÆv¶³(=||/öÜbó©vÊV2QCKû\ò–Xíë<´•„î6ƒ.”_±•I¢€IÝ”eÿuz)v"MïtåÂáR¿¨ïw³]]Œ]ëôíîkîg~; ðü>_ô»ïj5…v¼KÆNS¬£vµ¦Úi÷•éKó»¨úm pC•¸¶WÚ®¶¦t” ‰Öö½mùs§üy$ì¹w8ØòæNyÃ:Þˆ3—0%wÎ\È”s²ÚÛˆWòÅT[ºÜ1]LõÐÙ‘àêÀDíåÇõü>˜ÒÁþîK[³Qjx#=èÓÛ ~»w»ú.³L‡~!ÿêCíÚøP[×6F›¦úíX·øÞ'¾2aÀYÐ~ÇÃ—b—aŒ°UgIÞX=ShkWTKÚj»ÌÏµ¿ÐFkßóuÑŽº&ìJÉâ­ä©TâÝª|?ìTåa¬¨#r$ÞiÿŸD"W7@Õ._,ñÿ×Aï®aØÓ+¼²Î_ˆ¹äæîHTŽGsºêÔÚí·át~×`°˜“#~9~¬h`¤(G/ûàaøûHÜØàAý&Ÿ1mÝ»d~»ioìÃÏoß{Áž½K¼×²u£7ú?Õ—ý-°k   0707010005c840000081b40000000000000000000000015ab2175400000eb1000000660000000000000000000000000000004000000000root/usr/share/doc/opensvc/template.service.fs.reiserfs.conf.gz   ‹      í]ksÛ6ýž_™LÆÎ¬¤µ³Íç1õn“Ì¶MgÝf?t:6D‚Ö$Á du»ÿ}Ï½ )P~HŽ±»ÒL[$/Á{Î}—ÈÓ§·ùyòTÜâ‡Äev Ü¢R¢VÚª:³7w‹ˆ»]Ý=ù9³O÷~yòŠŸ÷M÷ÄOžÐÈOÕbnêô`9€¤VÒ©cS9mJ‹S†·ø!]ÙÄTrœ«îž?Ö¢µúµÑµZæ½Ì-©j3ÓãÑåä ¾$U™lr·þ÷¦äºœªZ;Y&á>¹’™x+¦J¦í ”À“«Ú·•Jt¦œ3Q%®Mè´Ä”3UGòí4Wg¬9ÜÛ&8p˜¦šT%st&œ•´ü¯›*‘›‰Npxfò¦PAÁ]²LG}ŽôoJà;QJœ'küÉqr*¬r8ïÉ«>6 s8Äž4¦±bq¤³Éÿ5ŒX?V®ÉšTÛS1©MSñ¯™Î•]X§
\0Ó‰Þ¦ÉS1VLÝc¨Tdµ)šÙpdÆ\†¶_…öÀ²%VÉ
:ôe‹ÎŠáYµƒ‡ªkœ£m„ÙHF(± uVÕÊò8ÿuùöÙÞÿóÝ»ßøtôû§¿ÿ—‘ã¯Ä\x˜Ðu–u¯4ÎMrÚ©Ÿt™Ž.ä$˜ë0XB3‚&:˜¦)ÝH|£³LÕªt­äD–°;ò}æ	ÓöŒHTi00ÑØpºökúŽÜ.J'É[¿Â×WCü›××—Ãør'ùe@&…xWÉŽÑbÄP>Å´‘ø*ƒö(*£ñ·¶8µª$á?‰c>’ÜJº©¨q™eä{]	OÁ 9t˜ÆŸC¼êìh%Œ±z¡¼µÚ¥œbËÿs*™V«FÒÒš0_Êêø‹Çú‡¤ÌÙjœ†“ãF#­‚S—¬2z¬Õp Òó&¥tz¦òÅò‚ÝS7;£¿G£Ñs#¸&Ž9ÙF­)<ô£Oxr±¿÷¬M4¢±q6 kBz^5Î{?º!AÇGD8ÏhâÖ>?Gºè9DÂ8SQ#%C.Àè' fMS'Ê®
kJÍ‚¾S9^8å™×‘êjî•æÙŽ**w,Ç¦~|öÜ¹­Œµ”Nˆ=XŽVü¾¼Ï¹”tlnU®˜rpbÇáúøX©òèÓßÄ\ç¹*Ç˜¬&`U=“ä4pQNöêf¨üð”`ª;Pvé ÿ!GâÈPC¢¬'*N}tQåª@x•¾L€q4U…K½-äÒ:¸²¶¢ìÙóÑòa"A;éc‡ŽPhßY%ÓN›ÉfÈ,gë­T=ì…|Ïl1–V{ö®J‰= —Ð¸š'UóPg>Ó^ƒµ©½R»ªþjÑâgó/ýˆ<7sAÄ£´#†cáÉ‡±.	%¸Löbþn ŽÔ;Ãß4Éû²î	îhpœ¨e‰tþ@ìö/è‹½áÂ5à8é‹ËÁ,T±óîÀ„zM½ð…P<™ô]ô=Ï%]\Âm-¸`À±Büâ«!¾ Ø {WU÷àÿ‡ªK•w9ÊLæˆXš*\•ëëdŠŸ™ƒøzïÅWÂë¾½ì…@3#‹M‘¶¸†ƒÄ@¨Ò6uÈjÇR‚ãÇ=+ø(\½vqòÅ÷ H©ää½—çô…"D‘ A4¢²?¨„"DGŽö#‰à !(m0Œº‡=J}/÷×±ï×Æ8¹%ß¦þ… HÁøA
•êïÄø2’ðIh‚:ì­ò³¤:ÏâÄ.Ý
È…§åkäØ"‡‡Ï_H¢¤:'0Nd-åT=ïáˆÔ™¤g@$úV—Í™Hø
äúÃ}x=‰”§)s]hG÷æœ)–fÀzÔ«cñÒ8Ê¦ƒ~xüB'µ±
šÚX‘8%axÐ›6éH¼Ü{ö5Î	²^ú:‚¾ Z?:çEïŒHš›nÌy&2QÞK¿:¢SÓÜmò-ù¯$ÿ†ž÷Pd¹œˆÝ=Š‚ûTKJ'|gé«T[ÿ3qçcãócOQ´N>MNËÀWÎ¹ÛBpwïù ™¼=µ^¾tŽ³~.8ášï#Q!¼³KÆaYÈûPúY…J5;áò“ùñ»H¤÷Ø8~'n‡ËÚ¸õ)¨lžÁO°Æ¦?2ÛŒ}éüŠž"h	ÑÑ
Ø*±?Zæ1kÁôý5gãÞ²ûºì>·®òŽƒ¾Ý$êŸË+wuáÎsÏ‰7ü˜{6€ñƒÌµžÀºxÖU£U+ÎÒZ…*cn0Ú>äïñg;ì\V•.!lK‘ëRä"xè¤Xj5L¿Ð¶¶cÜ¢+Þˆ¯®Bm¶5êaÔ"ÈîÚ²ýjnwç}5(;Ç
$¢§®ýðbûo‰5‹=U9k]Â8?Õæx®ôdº¥×­8„¿ò:ï‡xÄœçÞ…×îH|"0 ¤›+UNy÷Ð(T°+º‡åãß-Îy/C”ròÎeï]
ç-tN\„Wâ*ëxZÚQÒhZõÁ±w3³È#u?1VyˆÉ$-—ŒerÛ²¯þ
<«£l6¡ùF¯ùF\
š““Ç”Q¢îþüËó»ÄÍÒ~¯æðópkl$ë±vµ$èð{;ãNs?„‡w‡‰0„Þ<~·ª––£‚´°”ÐÉÕeÆú¶‰æW0c5•3q½bœ–üýw„œrY–(7´Á—OˆIþŒ¸ªMÝ†WèšÆîPz‘´QŒ¿¢'-ÿ,™j<Ú72—­¹¢QÏCÜŽÏÙ„À;ñs,±ÓlÆò:¯)¿¦È?®éðÆÿè4û0×‘žÍËV©´*Ü‚6çV€‹¦ì•N4þÕYì‡{%¸.O-
íe"õIQ î1€ŒRÂ±jƒJ}?Cpík–ãBEÿ#Á—@ß+rwà«eB[™<à´ÓÆuâÕÎ¶þRÃ	çs¹ -Zù… “xÐ““‘/êù}óîýáOßþè[¶æS“÷VýÂ=!¨ã(ï¹êbj0^‹2E9Sÿï{¬jíMTÕÊ55ž¶I¨¦Ä8’"=8±M4;KŠI-†V¼ÆÔŒö¶Õ‰_
ƒ³âàt~pžpÔÇô[ÑÚØ¸|,ŽZÚ?/šG^dyûHÎr ~Öìd©ÿÅy˜ý$o;1¶ÆÚ|Ã‘||sºl<W í©§í¢LÂšÀÃtÈ?LÂ„kS¿Hê[<h¾7tE€G¿°ÎUËíÕ¾Tå6ÕˆqÓåp™Ô9×ðçÅåÁÀw‚PAMÎaF³·?Y•59+zIé\Ÿ‚wMQEL1i³eÇ\lI2n ÛeYPLÇ‹GGÔ‡ÉÅCïÁ½Oã÷€&âK^áÜ6DƒöV¤R!’¯6ÞZÑ“;k]5¥æ­Œ¨í°Ÿ*øŽëžK÷—ø^=Ÿ5¢`‰¯9Xc<õ¡nQq3Š—¸?ˆ'hÕ,Û¡÷'Ø®JSyÉ7å72 I¯‚Â8ßaì§"9ÜžéË"ò½d5NÐuM	Èò,£Ø2‰¹¬™âÔ.nõý­—žù /Ú9cjõ³®«©eè£3˜VØƒ²—#ß‘Ç>š×—OuÕÏ{ÛòmrlMcÐ~ºdå_"=Õp“¦¬©sÝ$<†Ç¬CÏyÞ¾£-¤cø©ìÉé²„S¥*Z4Õ¢í>íò+é|Ù»·wÐ—qªÿD[VÝ«"­ŸgOD†›0 ¾ÇÕð¾éÑÁÿ0£pÀŸ¯½§Òï!Ý›J^è\ ˜!¨§ÔwƒÐA]´”ÜµX¶Ÿ."|Ž7›pã¼£q~"R»¨¿—hJêÎ:Ìó‹¤…en’ç¨Þ^ gH*uEp*z®ÌO#„‹×„?U&²Ú²ô.Xêg|¿waèµòYu\¨³ÕZNtJ- ]k£Ô»Æç˜K±4SÛXP¹LóUCÏ	¯@0Ár³ðë„±ƒ#©RC¾(æû­ÏœÔñcŽtwõbÍa÷"eøI­}‘:S	µ»…åžûºÊ[Ä»º6u÷­1ÕuS¹È……ì¤‡ÇÕSÈ•±n‹ÞõÑ“™_¿uðVðXƒ ÞBw†·š1ÆS¹×0»ëÝ·››ÜmÀÖÃb½¹=În‡djaYâ&fÖö7¬3±-V73¯›BÕa°‰Y™ÇW~<(«êö>Û¨Lµ‘Mmº‘IÝ§«â` •ãmiv¿¥Ù¥À]†È¦8n«´{­Ò6ÀñZuZþ-Š÷S°mfŒŸeŠ[ï¥vÛÐ?Ç·¥ÁÝ–q›ßÅAê-lwZÑmhp×‚m[ÜÝqq·©©m^5lë¼;®ó6¶³u˜EèqÐàã{±çn›OµS¶’‰ZÚç’·Äj_ç¡­$t·t¡üŠ­LLê¦,û¯ÓK±iz§+—‚øE}¿›íêbüëZ§ow_s?óÛm„ç÷ù¢ß}W«)´ã]2všj`µ«5ÕN»¯L_šßEÕo€ªÄµ½Òvµ5¥£H´¶ïmËŸ;åÏ#aÏµ¸ÃyÀ–7wÊÖñFœ¹„)‘¸ëpæB¦œ“ÕvØF4¸’/¦ÚÒåŽébª‡Î–ˆW&j/?®ç÷Á”ö?p_ÚšRÃéAïŒè˜ÞðÛ½ÛÕw™e:ôùWj×Æ€Úº¶1Ú4ÕoÇºÅ÷>ñ•	+ Î‚ö;¾»c„­:KòÆê™@[¸¢ZÒVÛe¾xÎ¨ý…6Z³øž÷¨‹vÔ5aWªHo%O¥ïVåûa§*{`E‘#ñîLûÿŒ ¹ºjØ¨vùb‰ÿ¿zotÃž^á•uþBÌ%7wG¢r<šÓ…T§Ön¿§ó»ƒÅœñËñcE#E9zÙÃßGâÆê7ùŒ¸hëÞ%óÛM{c~~ûÞöì]ŠØà½–­ýC¸Ñÿž+ÇÚ´k     0707010005c841000081b40000000000000000000000015ab2175400000eb2000000660000000000000000000000000000004100000000root/usr/share/doc/opensvc/template.service.fs.reiserfs4.conf.gz  ‹      í]ksÛ6ýž_™LÆÉ¬¥µ³Íç1õn“Ì¶MgÝf?t:6D‚Ö$Á du»ÿ}Ï½ IPŽ-9~ÄîJ3Il‘¼ï9÷\"ßäçÑcqƒ—Ù]á–•µÒVÕ™ýêZânðq7«»G?göñÞ/^òó¾îŸøÑ#ú©Z.Lô#Hj%:6•Ó¦´8etƒR–ML%'¹êîùcÝ(:P«_]«~0ïdnùHU›¹¶.§ñ%©Êd“»~øß›’èr¦jíd™„ûäJfâ˜)™¶ƒPO®jÜV*Ñ™NpÎT•¸6¡ÓSÎUÉ·³\±æpo›àÀašjR•ÌEÐ™pFTÒò¿n¦Dn¦:Áá¹É›BCtQÈ2Gü9Ò¿)ïD)qž¬ñ'ÇÉ©°Êá¼G/‡Ø ÍÑ#tzÚ˜ÆŠåEÎ§ÿ×0F`ý8X¹&chRmOÅ´6MÅ¿f:Wvi*pÁ\'
x›&OÅDE0u¡R‘Õ¦`hæSÀ‘sØ~QÚ÷È–X%+èÐ—-:+†CfÕª®qŽ¶fcq¡Ä‚ÔYU+ËàüWå›'ÿy÷Ï·oÿáãÑïÿþ_FŽO¼;pá~B×YÖÐ$7Éik ¤~Òmd:ºÓp`f¬Ã`	Íšè\`Z˜¦tcñÎ2U«Òµ’YÂîÈ÷	˜'LÛ3"Q¥ÁÀDcÃMèÚ¯é;6r»,$oý__ño^__ã‹ä—™â]%;F‹CùÓÆâ}F¨ì¶G@Q¿µÀ©U%	/øIóÁäVÒÍDmŒË,[ ßëRx
ýË¡sÏ,0ÖøâUgG+aŒÕå­Õ.å[þŸSqÈ´Z5’–Ö„ùRVÇ_<Öß'e¾ÏVã4œ„˜4iœºd•Ñc­†{•7)¥Ós•/ûžþ›
¹ùý=ŸùÁ5qÌ1È6jMáa}Â“‹ý½'m¢³XÒóªqÞûÑ‘:>8&zÄyF{·öù9ÒE?ÈÂÆ™Š‘(qF?0kš:QvUXSjôšÊÉÒ)Ï¼ŽT—s¯4ÇÈvTQ¹c91õÃ³çîÈMeœ¨¥t
DìA?Zñ{Ÿs)éÄÜª\%0å àÄŽÃõ;»âC¥Ê£ç"¨c²š€Uõ\’ÓÀQD99¨ü™¡òÃS‚q¨î@BÙ¤ü‡‹#Cy‰v²žª8õÑE•«áUúj05` ÆÑT.õ¶KëàÊRØŠ²gÏÆýÃD‚v2ÒÇ Ð¾³J¦6“	ÌYÎÖ[©z4ùžÙb"­öì]•{ . q5=Nªæ¾Î<|¦½kSz¥¶ªþj
Ñâgó/Ãˆ<7AÄ£´#†cáÉ‡‰.	%¸Löbþn ŽÔ;Ãß4Éû²î	îhpœ¨e‰tþ@ìíîï>§/öFÏ	×€à¤/.³PÅÌÛê5õÒBñdÒwÑ÷<—tUp	·µà‚Çvñ[ˆ/‡øÁÙ»šªz ÿ?T]ª¼ËQæ2GÄÒTáªt¹¾Nfø¹‘9x€¯÷÷ž%¼þáÛËA¸4£	²Øi‹k8Hì
UÚ¦YMàXj@rü¸b…«×.N¾ø© •œ¼÷òœ¾PDÀ€($ˆFTö•P„èÈÑ~$$¥†Q·ãðÁb@)°ïÅþ:öýÚ'·äÛÔ¿`)?HA Rý_F>	MP‡‚½uA~–TgâIœƒØÞ­€\xZ¾FN,rxÈðü…$J:0 sãôHÖ²PNÕcñŽHIJpv‰Dßê²9	_\´¯'‘ò4e®íèÞœ3ÅÒXzau,þBGÙÐ_è¤6VÁ@S« §$zÓ&‹{O¾Æ9AÖ_GÐ¤CëÇBç<œIsãÏ9ÏD&Ê{é—GÔccŠcšû¯M¾%ÿ¥äßÐóŠ,—Sñt¢à>Õ’Ò	_ÀYú*ÕÖÿLÜùÐ8Â<ÄØSÔ$­“O“Ó2ð•sî¶|º÷l™¼=µ^¾tŽ³~.8ášï#Q!¼³KÆ—aYÈûPúY…J5;áò“ùá»H¤÷Ø8~'n‡ËÚ¸õ)¨lžÁO°Æ¦?2ÛL|éü’ž"h	ÑÑ
Ø*±?îó‰˜µ`úþŽ³qoÙ}UvŸ[WyËAßnõÏå•OuáÎsÏ‰7ú6€ñƒÌµžÂºxÖU£U+ÎÒZ…*cn0Ú>äïñg;ìBV•.!lK‘«RäSðÐIÑk5L¿Ð¶¶cÜ¢+^‹¯.Cm¾5ê{aÔ"ÈnÛ²ýjnwç}5(;'
$¢§®ýðbûo‰5=U9k]Â$?Õæx¡ôt¶¥×8„¿ò:ïûxÄœçÞ…×îX|$0 ¤[(UNy÷Ð8T°+º‡åãß-Îy/B”ròÎeï]çtN|
-®Ä§TÖñ´´£¤Ñ´êƒcïff‘Gê~b¢2ò’IZ.™Èä4¶e_ýxVGÙlBóŒ^+òµ¸4'§/(£D}úó/Ïn7KøƒšÃÏÃe¬±±8¬'ÚÕ’ ÃïíŒ;ÍýÞz$ÂóøÝªRXzèGia)¡“«Ë6Œôm5Ì;®`&j&çâÅ8-øûï97å²,Q8nhƒ/Ÿ“üq9T›"º¯.Ð5Ý¡ô"i£EOZþY2Õx´¯;d.ZsE¢ž‡¸Ÿ³	wêçX:c§ÙŒþ:¯)¿¦È?®éðÆÿà4{?×‘ž-ÊV©´*Ü‚6çV€‹¦ì•N4þÕYì‡%¸.O-
í¾G‘ú¤(@ÆN)áDµÁ@¥¾Ÿ!¸ö5Ëq¡¢ÿƒ‘àK ï¹Š;pÕ2!È­‡Lž
pÚYãˆ:ñêÎ{g[©á„ó…\R‹­üBÐÉ	<èÉÉØuÈü¾yûîð§oô-[‹™É«~ážÔŽq÷\u13¯‰E™Æ¢œ©‚ÿ÷½Vµ¿&ªjåšOÛ$TSbœI‘œØ&š'Å´#+^áGjF{ÓŽêÄ/…Á…Yqpº88	O8nã	†­hmlì‹£–öÄÁ‹æ‘ûô·äôñ³f'½Vüçaö“¼íÄØkóGòáÍé²Qð\´§ž¶Ë2	k÷Ó!ÿ`0	®Mý"©oñ ùÞÐþÂ:W-wPûR•ÛTklxWLš.‡Ë¤Î¹†?/.7¾„
jrsš½ýÉª¬ÉYÑ=¥s}
Þ5E52Å¤ÍúŽ¹Ø’dÜ ¶‹². ˜N–Ž¨÷“‹‡Þƒ{ŸÇï]šˆ/94x…sÛÚ;X‘J…H¾ÚxkADOî¬uÕ”š·2¢´Ãaªà;®.Ý_â{õ|Öˆ‚%n¼ä`ñÔ„ºeÅÍ|(^âþ ž4¢UC°l‡ÞŸ`»*M9â%ß”ßÈ€&½

ã|‡±Ÿ6Šäp{¦/‹È÷’qdÔ8A×5% Ë—4²ŒbËx,²fjˆoP»¸ÕGô·î*<óA^<´sÆÔêg]WSËÐg0­°{e/G¾#}4¯/Ÿêj˜÷·þmrlMcÐ~ºdå_"Õp“¦¬©sÝ$<†Ç¬CÏyÞ¡£-¤cø©Èé²„S¥*Z4Õ²í>íò+é|ÙÛÛ;è‹85|¢-«î†U‘ÖÏ³'"Ãußãrxßôàà¿ŸQ8àÏ×Þ€Si‹÷îÍ¤/t.ÌÔSê»Aè .ZJîŽZ,ÛO‘v}Ž7Ÿrã¼£q~"R»¨¿—hJêÎ:ÌóOIËÜ$ÏQ}ÿ¼@(ÎTêŠàTô\;˜ŸF¯	ªLdµeém°ÔÏø~ïÂÐkå³4ê¸Pg«µ:5œè”Z@ºÖ,FipÏ1{±4SÛXP¹LóUCÏ	¯@0Ár³ôë„±ƒ#©RC¾(æû­ÏœÔñCŽt·õbÍa÷"eøI­}‘:S	µ»…åûºÌ[ÅÛº6u÷­1ÕuS¹È……ìd€ÇåSÈ•±n‹ÞÕÑ“™_¿qðVðXƒ ÞBw†·š1ÆS¹W0»«Ý·ë›ÜMÀ6Àb½¹=Ìn‡{djaYâ:fÖö7¬3±-V×3¯ëBÕa°‰Y™‡W~Ü+«êö>Û¨Lµ‘Mmº–I]§Ëâ` •ãmiv·¥Ù…À]„È¦8n«´;­Ò6ÀñJuÚ þ-ŠwS°mfŒŸeŠ[ï¤vÛÐ?Ç·¥Áí–q›ßÅÁê-l·ZÑmhpW‚m[ÜÝrq·©©m^5lë¼[®ó6¶³u˜EèqÐàÃ{±çv[Ì´S¶’‰YÚç’·Äj_ç¡­$t·t¡üŠ­LLê¦,‡¯ÓK±iz§+{Aü¢¾ßÍvu1þU­Ó7O_q?ó›]ÚÏïóE¿û®VShÇ»dì4Õ®uÔ®ÖT;í¾2Ci~U¿ n¨×öJÛÕÖ”Ž ÑÚ¾·-n•?„=Wâç[ÞÜ*oXÇqæ¦Dâ®Â™O2åœ¬¶Ã6¢Á¥|1Õ–.·LSÝw¶D$¸<0Q{ùq½¸¦t°ÿûÒÖl”ÞHzgD'ô6€ßîÝ®¾Ë,Ó‘ß@È¿úP»6~ÔÖµÑ¦©~;Ö-¾w‰¯LXp´ßñè…xÊ0FØª³$o¬ž«]hk
WTKÚj»Ì—Ïµ¿ÐFkßóuÑŽº&ìJÉâ­ä©TâÝª|?ìLåa¬¨#r,ÞžiÿŸD"W7@Õö/–øÿë`ðF×(ìé^Yç/ÄBrsw$*Ç£9]È@ujíöÛp:¿k0XÌÉ¿?Q40R”£—}ð0ü}$nbð ~“ÏˆOmÝÛ3¿Ý´7öáç·ïýÄž½½ˆÞkÙºÑ?„ý)!Zµk    0707010005c842000081b40000000000000000000000015ab2175400000eaf000000660000000000000000000000000000003c00000000root/usr/share/doc/opensvc/template.service.fs.tux3.conf.gz   ‹      í]ksÛ6ýž_™LÆÉ¬¤µÓæ‹ó˜zÛd'³MÓY·ÙŽ‘ „5I°(YÝîßs/@
”’ãGì®4“ÄÉKðžs_À%òøñM~=7ø!q™·¨”pÍéW×wƒˆ»YÝ=ú%³w}ô’Ÿ÷5?ñ£G4êµ˜›:Ý_Þ<©•têÈTN›Òâ”á~HO61•çª»çOu£è@­~kt­–ƒy'sËGªÚÌ´Åxt9Ù/IU&›Ü-‡ÿƒ)ù€.§ªÖN–I¸O®d&Þˆ©’i;%ðäªöÇm¥éçLT‰k:-1åLÕ‘|;ÍÕ)k÷¶	¤©&UÉ\	gD%-ÿë¦Jäf¢ž™¼)TP0D…,ÓQDCý»øN”çÉrœœ
«Î{ô²€1B§'i¬X\élòcÖO€ƒ•k2†&ÕöDLjÓTük¦seÖ©Ìt¢€·iòTŒUS÷*Ym
†f6™1a`íE¡=pl‰U²‚}Ù¢³b8dVíà¡êçha6J,HVµ²|ÎU¾yòŸwÿ|ûö?þñéïÿeäøÄK±î'teÝ©s“œ´Bê'ÝF¦£9	¦Æ:–ÐŒ ‰Î¦…iJ7ßé,Sµ*]+9‘%ìŽ|Ÿ€yÁ´=#UL46Ü„®ý†¾c#·‹ÒIòÖ/ñõåÿîõõå0¾ØI~I!ÞU²c´1”O1m$Þg„Ê =Â ŠÊhü­mN­*IxÁOâ˜†$·’n*jc\fÙù^—ÂS0è_{f±Æç¯:;Z	c¬^(o­v)§ØòÿŒŠC¦Õª‘´´&Ì—²:úâ±þ>)ó}¶§á$Ä¸ÑH«àÔ%«Œk5Ü#¨ô¼I)ž©|±¼àé¿©†›Òß£Ñè™¿\Çƒl£ÖúÑ'<¹ØÛ}Ò&ÑØ8€5!=¯ç½Ý‰ ãƒ#¢Gœg´qkŸŸ#]ôƒ"aœ©¨‰’!_ô ³¦©eW…5¥fAÔDŽNyæu¤ºœ{¥9B¶£ŠÊÉ±©ž=wGn*ãD-¥S b÷—£,ïs&%ƒ[•«¦œØq¸~g >Vª<üô­˜ë<Aå“Õ¬ªg’œŽ"ÊÉ^ÝàÏ•žŒCuÊî à?„àHÊcH´“õDÅ©.ª\¯ÒWƒ©0Ž¦ªp©·…\ZW–ÂV”=}6Z>L$h'#}ìÁ± 
í;«dÚió1™À™ål½•ª‡½ï™-ÆÒjÏÞUI ±àW“£¤jîëÌÃg:Ð+°6U Wj—‚CÕ_M ZüBcþµ?‘çf.ˆx”6cÄp,<ù0Ö%¡—É^ÌßÀ‘zGâ[üM“±/ëN‘àŽwÀ‰Z–Hç÷Åî`oðœ¾Ø>'\N€“¾¸ÌB[0oL¨×Ô_Å“I¢ïy.éªànkÁŽìâ·_ñ9ÁÙ»š¨ºÿ?T]ª¼ËQf2GÄÒTáªt€\_'SüÜÈ<À×{»Ï¿^ÿðíe/Ü šáYlŠ´Å5$B•¶©CV8–„?îXÁGáêµ‹“/¾E*H%'ï½<§/0 Š	¢•ýA%!:r´I	AiƒaÔí8|°èQ
ì{±·Ž}¿5ÆÉ-ù6õ/X@
ÆR¨T'Æ—‘„OBÔ¡`o]Ÿ%Õ™xç véV@.<-_#Ç9<dxþB%Ðqz$kY(§ê‘xG¤N%%8"Ñ÷ºlNEÂW ×îÁëI¤<M™ëB;º7çL±4Ö£^X‹¿ÆQ6ÅôÃã:©U0ÐÔÆ*ˆÄ)	ÃƒÞ´IGâÅî“opNõÂ×ôéÐú±Ð9Ï{gDÒÜÜøscÎ3‘‰ò^úåõÈ˜âˆæþk“oÉ)ù7ô¼"ËåD<Ý¥(¸Gµ¤tÂp–¾Jµõ?w>6Ž01öu IëäÓä´|åœ»-Ÿî> “·'ÖË—ÎqÖÏ'\sá=p$*„wvÉ¸ó",yJ?ë¢P©Æcça \~ò ?~ˆùAz#áw²àvp¸¬;PŸ‚ÊáükL`š!ñ#³ÍØ—Î/é)‚–=­€­{£e>³Lß[Ãq6î-»¯Êî3ë*o9èÛM¢þ™¼ò©.#ÜyNâY Qã†³aÏ0~¹ÖXÏú¢j´jÅùQú@ë£PeÌFÛ‡ü]þ¬a‡ËªÒ%„m)rUŠœç K­†€éÚVÂvŒ[tÅkñõe¨Í¶F}/Œú/Ùm[¶_Ííî²¯eçXDôÔµ^lÿ-±f±G *g­Kç'ÚÍ•žL·ôº‡ð7^ç}ÿ˜óÜ»ðÚ‰Oæ>€ts¥JàÃ)ï …
6bE÷°|<à£Å9ïEÈ‚RN>À¹ìÝá¼Î‰óÐâJ|BeOK;JM«>8önfy¤.à'Æ*#‘!™¤å’±LNb[öÕ_gu”Í&4Àèµ"_‹Asròð‚2JÔ§¿üúì6q³4ß«9ü<\Æ‰ƒz¬]-	:üÞÎ¸ÓÜááÝ¡G"¡7ß­*…¥‡å¨ -,%tru™Á†ñ€¾m¢†yÇÌXMåLC\¯§eÿ!gF£\–%J Çmðåb’?#.‡jSD·áÕº¦±;”^$mã¯èIË¿J¦öu‡ÌEk®¨@ÔÃó7ãóc6!ðNüKgì4›±¼ÎkÊ¯)òkú¼ñ?8ÍÞÏuÄ¤gó²U*­
· ‡Í¹à¢){@¥uûá^	®Ë‹B{Ù£H}Rˆ{ c§”p¬Ú` RßÏ\ûšå¸PÑÿÉHð%Ð÷Š\Å¸Àj™äÖC&Ï@8í´qDxuç½³­¿ÔpÂù\.¨E‹V~!èøôøxä‹:d~ß½}wðó÷?ù–­ùÔä½U¿pOjÇ8ŠÇ{¦º˜ŒW„Ä¢LcQÎTÁÿûÞ«Ú_{UµrM§mª)1Îý¤H÷m“ÆÍÎ’bR‹¡¯ð#5£½iGuì—ÂàÂ¬Ø?™ï‡'µÇñýV´66.‹£–öÄÁ‹æ‘—YÞ>’³ˆŸ5;^jÅqf?ÉÛNŒ­±6ßp$Þœ.ÏH{âi»(“°&p?ò“0áÚÔ/’úšï]àQà/¬sÕr{µ/U¹MµÆ†bÜt9\&uÎ5üYq¹A0ð TP“s˜ÑìíÏVeMÎŠ^R:×'à]STQ#SLÚl™Á1[’ŒÀvQÖÓñâÁõ~rñÀ{pïSãø= ‰ø’CƒW8·Ñ ½ƒ©Tˆä«·DôäÎZWM©y+#j@;è§
¾ãºçÒý%¾WÏg(XâÆë@ÖOý@¨[TÜÌ‡â%îâI#Z5Ëvèý	¶«Ò”C^òMùhÒ« 0Îwûi£H·gú²ˆ|/GFt]S²|A#Ë(¶ŒFb.k¦Æ¾øµ‹[}Dë¥A…gÞÃ‹‡vÆ˜Zý¬ëjjúà¦v¯ìåÐwä±æõå]õó^à¶|[„[SÆ´Ÿ.YùW HO5Üd§)k@ê\7	Ï‚á1ëÐsGž·ïhéØ~.{rº,áD©ŠÖMµh»O»üJ:_ö.ÇíôEœê?Ñ–UwÃªHëgÙ‘á:ˆïq9¼ozpðßÏ(ðçkoÀ©´Å{H÷¦Ò†:fê)õÝ tP-%w‡-–í§‹HŸãÍ&Ü8ïh‡?„ˆÔ.ªÄï%š’º³òü<ia™€›ä9ª÷ãŸÅ’J]œŠžióÓáâ5áO•‰¬¶,½–úÙßïÝAz­|–FêtµV§†RH×šÅ(õ®ñ9æR,ÍÔ6T.Ó¼GÕÐsÂ+L°Ü,ü:aìàHªÔÐ‡/Šù~ë3'uô#Ým½XsÐ½H@~RkßG¤NUBínay§ç¾.óV#ñ¶®MÝ½G@kLuÝT.ra!;éáqùre¬Û¢wuôdæ—Ço¼<Ö ¨·ÐÝ€á­fŒñTîÌî*F·Åíú&w°õ°Xon³Ûá™ZX–¸Ž™µýëLl‹ÕõÌëºPulbVæá•÷Êªº…½Ï6*SmdS[ ®eR×Â©Eàr xhåh[šÝmiv!p!²)ŽÛ*íN«´p¼RÖƒ‹âÝl›ãg™âÂ;©Ý64ÃÏ1Âmip»eÜfÆ·AqÐ‡zÛ­VtÜ•`Ûw·\Ümjj›WÛ:ï–ë¼ílfQz4øð^ì¹ÄæSí”­d¢†–ö¹ä-±Ú×yh+	Ým](¿b+“D“º)ËþëôRìDšÞéÊ…ƒ¥ ~Qßïf»ºÿªÖé›§¯¸ŸùÍ€6Âóû|Ñï¾«ÕÚñ.;M5°ŽÚÕšj§ÝW¦/Íï¢ê·ÀUâÚ^i»ÚšÒQ$ZÛ÷¶åÏ­òç°çJÜá<`Ë›[åëx#Î\À”HÜU8s.SÎÈj;l#\ÊSmérËt1Õ}gKD‚Ëµ—Õó»`JûŸ¸/mÍF©áô wFtLoøíÞíê»Ì2ú„ü«µkãG@m]Ûmšê·cÝâ{—øÊ„ gAû_ˆ§c„­:MòÆê™@[¸¢ZÒVÛe¾xÆ¨}E­Y|Ï{ÔE;êš°+U$‹·’§R‰w«òý°S•‡=°¢ŽÈ‘x{ªýF‰\Ý 5lT»|±Äÿ_½7º†aO¯ðÊ:!æ’›»#Q9ÍéBªSk·ß†Óù]ƒÁbNŽøåø±¢‘¢½ìƒ‡áï#qcƒõ›|Æ@œ·uï’ùí¦½±?»}ï9{ö.Elð^ËÖþ)Üèÿ 'ô@°k   0707010005c843000081b40000000000000000000000015ab2175400000ead000000660000000000000000000000000000003b00000000root/usr/share/doc/opensvc/template.service.fs.ufs.conf.gz    ‹      í]ksÛ6ýž_™LÆÎ¬¤µ³Íç1õn“Ì¶MgÝf?t:6D‚Ö$Á du»ÿ}Ï½ )P~HŽ±»ÒL[$/Á{Î}—ÈÓ§·ùyòTÜâ‡Äev Ü¢R¢Éì-ˆ»ÅÄÝ®îžüœÙ§{¿<yÅÏû†žøÉô©ZÌM,ïÔJ:ul*§MiqÊð?¤&›˜JŽsÕÝóÇºQt V¿6ºVËÁ¼—¹å#UmfÚb<ºœÄ—¤*“Mî–ÃÿÞ”|@—SUk'Ë$Ü'W2oÅTÉ´„xrUûã¶R‰Ît‚s&ªÄµ	–˜r¦êH¾æêŒ5‡{ÛÓT“ªd.‚Î„3¢’–ÿuS%r3Ñ	ÏLÞ*(¢‹B–é(bÎ‘þM	|'J‰ód?9NN…Uç=yÕÇ8‡¡Ó“Æ4V,.ƒt6ù¿†1ëGÀÁÊ5C“j{*&µi*þ5Ó¹²ëTf:QÀÛ4y*Æ*‚©{•Š¬6C3› ŽÌ˜Ë0°Àö‹¢Ðx@¶Ä*YA‡¾lÑY12«vðPus´0‰Ã%¤ÎªZY¾ ç¿.ß>ûÏû¾{÷ûŸŽ~ÿô÷ÿ2r|â•ØºÎ²îÕ€Æ¹IN[!õ“n#ÓÑ…œ„ScKhFÐDçÓÂ4¥‰ot–©Z•®•œÈvG¾OÀ<`Úž‰*&nB×~Mß±‘ÛEé$yëWøújˆóúúr_î$¿È¤ï*Ù1ZŒÊ§˜62BeÐa Ee4þÖ6§V•$¼à'qÌC’[I7µ1.³l|¯+á)ô/‡Î³ÀXãsˆW­„1V/”·V»”SlùNÅ!ÓjÕHZZæKYñXÿ”ù![ÓpbÜh¤Upê’UFµîTzÞ¤”NÏT¾X^°ûo*áfgô÷h4zîo×Ä1Ç Û¨5…‡~ô	O.ö÷žµ‰F46Î`MHÏ«ÆyïG7D"èøàˆèçíAÜÚççHý ‡Cg*jD¢dÈµýÀ¬iêDÙUaM©YÐwj"Ç§<ó:R]Í½Ò#ÛQEåŽåØÔÏž»#·•q¢–Ò)±ËÑŠß—÷9—’ŽÁ­ÊUSNì8\¿3+U}ú›˜ë<Aå“Õ¬ªg’œŽ"ÊÉ^ÝàÏ•žŒCuÊî à?„àHÊcH´“õDÅ©.ª\¯ÒWƒ©0Ž¦ªp©·…\ZW–ÂV”={>Z>L$h'#}ìÁ± 
í;«dÚió1™À™ål½•ª‡½ï™-ÆÒjÏÞUI ±àW“ã¤jêÌÃg:Ðk°6U Wj—‚CÕ_M ZüLcþ¥?‘çf.ˆx”6cÄp,<ù0Ö%¡—É^ÌßÀ‘zGâoø›&b_Ö"Áî€µ,‘Îˆ½Áþà}±7|A¸œ '}q9˜…*¶`Þ˜P¯©¾Š'“¾‹¾ç¹¤ë‚K¸­8¶SˆßB|5Ädïj¢êüÿPu©ò.G™ÉKS…«Òr}Lñs#sð _ïï½øJxýÃ·—½ph†cd±)Ò×pUÚ¦YMàXj@rü¸b…«×.N¾ø© •œ¼÷òœ¾PDÀ€($ˆFTö•P„èÈÑ~$$¥†Q·ãðÁ¢G)°ïåþ:öýÚ'·äÛÔ¿`)?HA Rý_F>	MP‡‚½uA~–TgâYœƒØ¥[¹ð´|[äðáùI”t`@çÆé‘¬e¡œªGâ=‘:“”àˆDßê²9	_\¸¯'‘ò4e®íèÞœ3ÅÒXzau,þBGÙcÐ_è¤6VÁ@S« §$zÓ&‰—{Ï¾Æ9AÖK_GÐ¤CëÇBç¼èIssãÏ9ÏD&Ê{éWGÔccŠcšû¯M¾%ÿ•äßÐóŠ,—±»GQpŸjIé„/à,}•jë&î|labì)ê ’ÖÉ§ÉiøÊ9w[îî= “·§ÖË—ÎqÖÏ'\sá=p$*„wvÉ¸ó",yJ?ë¢P©Æcça \~ò ?~	òƒôGÂïdÁíàpYw >•-Â3ø	Ö˜À4CâGf›±/_ÑS-!z Z[%öGË|"f-˜¾¿†ãlÜ[v_—ÝçÖUÞqÐ·›Dýsyå®.#ÜyNây Qã†³aÏ0~¹ÖXÏú¢j´jÅùQú@ë£PeÌFÛ‡ü=þ¬a‡ËªÒ%„m)r]Š\ä  K­†€éÚVÂvŒ[tÅñÕU¨Í¶Fý ŒúOÙ][¶_Ííî²¯eçXDôÔµ^lÿ-±f±G *g­Kç§ÚÏ•žL·ôº‡ðW^çýð˜óÜ»ðÚ‰Oæ€ts¥JàÃ)ï> …
6bE÷°|<à£Å9ïeÈ‚RN>Â¹ì½Ká¼…Î‰‹ÐâJ|BeOK;JM«>8önfy¤.à'Æ*#‘!™¤å’±LNc[öÕ_gu”Í&4Àèµ"ßˆKAsròø‚2JÔÝŸy~—¸YšÀïÕ~.cÄa=Ö®–~ogÜiî‡ððîÐ#†Ð›ÇïV•ÂÒÃrT–:¹ºÌ`Ãx@ß6QÃ¼ã
f¬¦r¦!®WŒÓ²€¿ÿŽ3£Q.Ë%€ã†6øò	1ÉŸ—Cµ)¢Ûðê]ÓØJ/’6ŠñWô¤åŸ%SGû¦Cæ²5WT êñyˆÛñù1›x'~Ž¥3všÍX^ç5å×ùÇ5}Þøfæ:â!Ò³yÙ*•V…[‚ÃæÜ
pÑ”= Ò‰Æ¿:‹ýp¯×å©E¡½ìQ¤>)
Ä=±SJ8Vm0P©ïg®}Ír\¨èÿ`$øè{E®â\`µLrë!“g œvÚ8¢N¼ºóÁÙÖ_j8á|.Ô¢E+¿trzr2òE2¿oÞ½?üéÛ}ËÖ|jòÞª_¸'µcÅã=W]LÆ+BbQ¦±(gªàÿ}oƒUí¯½‰ªZ¹¦ÆÓ6	Õ”çAR¤'¶Iã€fgI1©ÅÐŠ×ø‘šÑÞ¶£:ñKapaVœÎNÂŽÚãx‚~+Z—ÅQKûGâàEóÈË,oÉYÄÏš,µâ¿8³Ÿäm'ÆÖX›o8’oN—‚ç
¤=õ´]”IXx˜ùƒ€I˜pmêI}‹Í÷†®ð(ðÖ¹j¹½Ú—ªÜ¦ZcÃ1nº.“:çþ¼¸Ü øN*¨É9Ìhöö'«²&gE/)ëSð®)ª¨‘)&m¶Ìà˜‹-É@Æ`»,ëŠéxñèˆú0¹xè=¸÷©qüÐD|É¡Á+œÛ†hÐÞÁŠT*DòÕÆ["zrg­«¦Ô¼•5 öSßqÝséþß«ç³F,qãu kŒ§~ Ô-*næCñ÷ñ¤­‚e;ôþÛUiÊ!/ù¦üF4éUPç;Œý´Q$‡Û3}YD¾—Œ#£Æ	º®)Y¾ ‘e[F#1—5Sã@|ƒÚÅ­>¢¿õÒ Â3„áÅC;gL­~Öu5µ}tÓ
{Pörä;òØGóúò©®úy/p[¾-BŽ­)cÚO—¬ü+P¤§n²Ó”5 u®›„gÁð˜uè¹#ÏÛw´…tl?•=9]–pªTEkƒ¦Z´Ý§]~%/{—ãöú2NõŸhËªûaU¤õóì‰ÈpÄ÷¸šÞ7=:øføóµ·àTÚâ=¤{SiÃ3õ”ún:¨‹–’»£ËöÓE¤Ïñfnœw4Ž£ÃïCDjUâ÷MIÝY‡y~‘´°LÀMòÕûñÏ„âI¥®NEÏµƒùi„pñšð§ÊDV[–ÞKýìï÷î ½V>K£Žu¶Z«SÃ‰N©¤kÍb”z×øs)–fj*—iÞ£jè9á&Xn~0vp$UjèÃÅ|¿õ™“:~Ì‘î®^¬9ì^$ ?©µï#Rg*¡v·°¼Ós_Wy«‘xW×¦îÞ# 5¦ºn*¹°ôð¸z
¹2ÖmÑ»>z2óËã·Þ
kÐÔ[ènÁðV3Æx*÷fw£Ûâvs“»ØzX¬7·ÇÙíð€L-,KÜÄÌÚþ†u&¶ÅêfæuS¨:61+óøÊeUÝÂÞg•©6²©-P72©áÔ"p5P¼´r¼-Íî·4»¸ËÙÇm•v¯UÚ8^«NëÁ¿Eñ~
¶ÍŒñ³Lqá½Ônšáçá¶4¸Û2n3ãÛ 8èC½…íN+ºîZ°m‹»;.î65µÍ«†mwÇuÞÆv¶³(=||/öÜbó©vÊV2QCKû\ò–Xíë<´•„î6ƒ.”_±•I¢€IÝ”eÿuz)v"MïtåÂáR¿¨ïw³]]Œ]ëôíîkîg~; ðü>_ô»ïj5…v¼KÆNS¬£vµ¦Úi÷•éKó»¨úm pC•¸¶WÚ®¶¦t” ‰Öö½mùs§üy$ì¹w8ØòæNyÃ:Þˆ3—0%wÎ\È”s²ÚÛˆWòÅT[ºÜ1]LõÐÙ‘àêÀDíåÇõü>˜ÒÁþîK[³Qjx#=èÓÛ ~»w»ú.³L‡~!ÿêCíÚøP[×6F›¦úíX·øÞ'¾2aÀYÐ~ÇÃ—b—aŒ°UgIÞX=ShkWTKÚj»ÌÏµ¿ÐFkßóuÑŽº&ìJÉâ­ä©TâÝª|?ìTåa¬¨#r$ÞiÿŸD"W7@Õ._,ñÿ×Aï®aØÓ+¼²Î_ˆ¹äæîHTŽGsºêÔÚí·át~×`°˜“#~9~¬h`¤(G/ûàaøûHÜØàAý&Ÿ1mÝ»d~»ioìÃÏoß{Áž½K¼×²u£7ú?8S›m¯k     0707010005c844000081b40000000000000000000000015ab2175400000eae000000660000000000000000000000000000003c00000000root/usr/share/doc/opensvc/template.service.fs.ufs2.conf.gz   ‹      í]ksÛ6ýž_™LÆÎ¬¤µ³Íç1õn“Ì¶MgÝf?t:6D‚Ö$Á du»ÿ}Ï½ )P~HŽ±»ÒL[$/Á{Î}—ÈÓ§·ùyòTÜâ‡Äev Ü¢R¢Éì‹›‹»ÅÄÝ®îžüœÙ§{¿<yÅÏû†ŸøÉõ©ZÌM,ožÔJ:ul*§MiqÊð?¤'›˜JŽsÕÝóÇºQt V¿6ºVËÁ¼—¹å#UmfÚb<ºœÄ—¤*“Mî–ÃÿÞ”|@—SUk'Ë$Ü'W2oÅTÉ´„xrUûã¶R‰Ît‚s&ªÄµ	–˜r¦êH¾æêŒ5‡{ÛÓT“ªd.‚Î„3¢’–ÿuS%r3Ñ	ÏLÞ*(¢‹B–é(¢Î‘þM	|'J‰ód?9NN…Uç=yÕÇ@‡¡Ó“Æ4V,.ƒt6ù¿†1ëGÀÁÊ5C“j{*&µi*þ5Ó¹²ëTf:QÀÛ4y*Æ*‚©{•Š¬6C3› ŽÌ˜Ë0°Àö‹¢Ðx@¶Ä*YA‡¾lÑY12«vðPus´0‰Ã%¤ÎªZY¾ ç¿.ß>ûÏû¾{÷ûŸŽ~ÿô÷ÿ2r|â•ØºÎ²îÕ€Æ¹IN[!õ“n#ÓÑ…œ„ScKhFÐDçÓÂ4¥‰ot–©Z•®•œÈvG¾OÀ<`Úž‰*&nB×~Mß±‘ÛEé$yëWøújˆóúúr_î$¿È¤ï*Ù1ZŒÊ§˜62BeÐa Ee4þÖ6§V•$¼à'qÌC’[I7µ1.³l|¯+á)ô/‡Î³ÀXãsˆW­„1V/”·V»”SlùNÅ!ÓjÕHZZæKYñXÿ”ù![ÓpbÜh¤Upê’UFµîTzÞ¤”NÏT¾X^°ûoªáfgô÷h4zîo×Ä1Ç Û¨5…‡~ô	O.ö÷žµ‰F46Î`MHÏ«ÆyïG7D"èøàˆèçíAÜÚççHý ‡Cg*jD¢dÈÅýÀ¬iêDÙUaM©YÐwj"Ç§<ó:R]Í½Ò#ÛQEåŽåØÔÏž»#·•q¢–Ò)±ËÑŠß—÷9—’ŽÁ­ÊUSNì8\¿3+U}ú›˜ë<Aå“Õ¬ªg’œŽ"ÊÉ^ÝàÏ•žŒCuÊî à?„àHÊcH´“õDÅ©.ª\¯ÒWƒ©0Ž¦ªp©·…\ZW–ÂV”={>Z>L$h'#}ìÁ± 
í;«dÚió1™À™ål½•ª‡½ï™-ÆÒjÏÞUI ±àW“ã¤jêÌÃg:Ðk°6U Wj—‚CÕ_M ZüLcþ¥?‘çf.ˆx”6cÄp,<ù0Ö%¡—É^ÌßÀ‘zGâoø›&b_Ö"Áî€µ,‘Îˆ½Áþà}±7|A¸œ '}q9˜…*¶`Þ˜P¯©¾Š'“¾‹¾ç¹¤ë‚K¸­8¶SˆßB|5Ädïj¢êüÿPu©ò.G™ÉKS…«Òr}Lñs#sð _ïï½øJxýÃ·—½ph†cd±)Ò×pUÚ¦YMàXj@rü¸b…«×.N¾ø© •œ¼÷òœ¾PDÀ€($ˆFTö•P„èÈÑ~$$¥†Q·ãðÁ¢G)°ïåþ:öýÚ'·äÛÔ¿`)?HA Rý_F>	MP‡‚½uA~–TgâYœƒØ¥[¹ð´|[äðáùI”t`@çÆé‘¬e¡œªGâ=‘:“”àˆDßê²9	_\¸¯'‘ò4e®íèÞœ3ÅÒXzau,þBGÙcÐ_è¤6VÁ@S« §$zÓ&‰—{Ï¾Æ9AÖK_GÐ¤CëÇBç¼èIssãÏ9ÏD&Ê{éWGÔccŠcšû¯M¾%ÿ•äßÐóŠ,—±»GQpŸjIé„/à,}•jë&î|labì)ê ’ÖÉ§ÉiøÊ9w[îî= “·§ÖË—ÎqÖÏ'\sá=p$*„wvÉ¸ó",yJ?ë¢P©Æcça \~ò ?~	òƒôGÂïdÁíàpYw >•-Â3ø	Ö˜À4CâGf›±/_ÑS-!z Z[%öGË|"f-˜¾¿†ãlÜ[v_—ÝçÖUÞqÐ·›Dýsyå®.#ÜyNây Qã†³aÏ0~¹ÖXÏú¢j´jÅùQú@ë£PeÌFÛ‡ü=þ¬a‡ËªÒ%„m)r]Š\ä  K­†€éÚVÂvŒ[tÅñÕU¨Í¶Fý ŒúOÙ][¶_Ííî²¯eçXDôÔµ^lÿ-±f±G *g­Kç§ÚÏ•žL·ôº‡ðW^çýð˜óÜ»ðÚ‰Oæ€ts¥JàÃ)ï> …
6bE÷°|<à£Å9ïeÈ‚RN>Â¹ì½Ká¼…Î‰‹ÐâJ|BeOK;JM«>8önfy¤.à'Æ*#‘!™¤å’±LNc[öÕ_gu”Í&4Àèµ"ßˆKAsròø‚2JÔÝŸy~—¸YšÀïÕ~.cÄa=Ö®–~ogÜiî‡ððîÐ#†Ð›ÇïV•ÂÒÃrT–:¹ºÌ`Ãx@ß6QÃ¼ã
f¬¦r¦!®WŒÓ²€¿ÿŽ3£Q.Ë%€ã†6øò	1ÉŸ—Cµ)¢Ûðê]ÓØJ/’6ŠñWô¤åŸ%SGû¦Cæ²5WT êñyˆÛñù1›x'~Ž¥3všÍX^ç5å×ùÇ5}Þøfæ:â!Ò³yÙ*•V…[‚ÃæÜ
pÑ”= Ò‰Æ¿:‹ýp¯×å©E¡½ìQ¤>)
Ä=±SJ8Vm0P©ïg®}Ír\¨èÿ`$øè{E®â\`µLrë!“g œvÚ8¢N¼ºóÁÙÖ_j8á|.Ô¢E+¿trzr2òE2¿oÞ½?üéÛ}ËÖ|jòÞª_¸'µcÅã=W]LÆ+BbQ¦±(gªàÿ}oƒUí¯½‰ªZ¹¦ÆÓ6	Õ”çAR¤'¶Iã€fgI1©ÅÐŠ×ø‘šÑÞ¶£:ñKapaVœÎNÂŽÚãx‚~+Z—ÅQKûGâàEóÈË,oÉYÄÏš,µâ¿8³Ÿäm'ÆÖX›o8’oN—‚ç
¤=õ´]”IXx˜ùƒ€I˜pmêI}‹Í÷†®ð(ðÖ¹j¹½Ú—ªÜ¦ZcÃ1nº.“:çþ¼¸Ü øN*¨É9Ìhöö'«²&gE/)ëSð®)ª¨‘)&m¶Ìà˜‹-É@Æ`»,ëŠéxñèˆú0¹xè=¸÷©qüÐD|É¡Á+œÛ†hÐÞÁŠT*DòÕÆ["zrg­«¦Ô¼•5 öSßqÝséþß«ç³F,qãu kŒ§~ Ô-*næCñ÷ñ¤­‚e;ôþÛUiÊ!/ù¦üF4éUPç;Œý´Q$‡Û3}YD¾—Œ#£Æ	º®)Y¾ ‘e[F#1—5Sã@|ƒÚÅ­>¢¿õÒ Â3„áÅC;gL­~Öu5µ}tÓ
{Pörä;òØGóúò©®úy/p[¾-BŽ­)cÚO—¬ü+P¤§n²Ó”5 u®›„gÁð˜uè¹#ÏÛw´…tl?•=9]–pªTEkƒ¦Z´Ý§]~%/{—ãöú2NõŸhËªûaU¤õóì‰ÈpÄ÷¸šÞ7=:øføóµ·àTÚâ=¤{SiÃ3õ”ún:¨‹–’»£ËöÓE¤Ïñfnœw4Ž£ÃïCDjUâ÷MIÝY‡y~‘´°LÀMòÕûñÏ„âI¥®NEÏµƒùi„pñšð§ÊDV[–ÞKýìï÷î ½V>K£Žu¶Z«SÃ‰N©¤kÍb”z×øs)–fj*—iÞ£jè9á&Xn~0vp$UjèÃÅ|¿õ™“:~Ì‘î®^¬9ì^$ ?©µï#Rg*¡v·°¼Ós_Wy«‘xW×¦îÞ# 5¦ºn*¹°ôð¸z
¹2ÖmÑ»>z2óËã·Þ
kÐÔ[ènÁðV3Æx*÷fw£Ûâvs“»ØzX¬7·ÇÙíð€L-,KÜÄÌÚþ†u&¶ÅêfæuS¨:61+óøÊeUÝÂÞg•©6²©-P72©áÔ"p5P¼´r¼-Íî·4»¸ËÙÇm•v¯UÚ8^«NëÁ¿Eñ~
¶ÍŒñ³Lqá½Ônšáçá¶4¸Û2n3ãÛ 8èC½…íN+ºîZ°m‹»;.î65µÍ«†mwÇuÞÆv¶³(=||/öÜbó©vÊV2QCKû\ò–Xíë<´•„î6ƒ.”_±•I¢€IÝ”eÿuz)v"MïtåÂáR¿¨ïw³]]Œ]ëôíîkîg~; ðü>_ô»ïj5…v¼KÆNS¬£vµ¦Úi÷•éKó»¨úm pC•¸¶WÚ®¶¦t” ‰Öö½mùs§üy$ì¹w8ØòæNyÃ:Þˆ3—0%wÎ\È”s²ÚÛˆWòÅT[ºÜ1]LõÐÙ‘àêÀDíåÇõü>˜ÒÁþîK[³Qjx#=èÓÛ ~»w»ú.³L‡~!ÿêCíÚøP[×6F›¦úíX·øÞ'¾2aÀYÐ~ÇÃ—b—aŒ°UgIÞX=ShkWTKÚj»ÌÏµ¿ÐFkßóuÑŽº&ìJÉâ­ä©TâÝª|?ìTåa¬¨#r$ÞiÿŸD"W7@Õ._,ñÿ×Aï®aØÓ+¼²Î_ˆ¹äæîHTŽGsºêÔÚí·át~×`°˜“#~9~¬h`¤(G/ûàaøûHÜØàAý&Ÿ1mÝ»d~»ioìÃÏoß{Áž½K¼×²u£7ú?˜wsù°k    0707010005c845000081b40000000000000000000000015ab2175400000eaf000000660000000000000000000000000000003e00000000root/usr/share/doc/opensvc/template.service.fs.umsdos.conf.gz ‹      í]ksÛ6ýž_™LÆÎ¬¤µ³Íç1õn“Ì¶MgÝf?t:6D‚Ö$Á du»ÿ}Ï½ )P~HŽ±»ÒL[$/Á{Î}—ÈÓ§·ùyòTÜâ‡Äev Ü¢R¢)ljìMÅÝâânWwO~ÎìÓ½_ž¼âç}žøÉ÷©ZÌM,oŸÔJ:ul*§MiqÊð?¤)›˜JŽsÕÝóÇºQt V¿6ºVËÁ¼—¹å#UmfÚb<ºœÄ—¤*“Mî–ÃÿÞ”|@—SUk'Ë$Ü'W2oÅTÉ´„xrUûã¶R‰Ît‚s&ªÄµ	–˜r¦êH¾æêŒ5‡{ÛÓT“ªd.‚Î„3¢’–ÿuS%r3Ñ	ÏLÞ*(¢‹B–é("Ï‘þM	|'J‰ód?9NN…Uç=yÕÇP‡¡Ó“Æ4V,.ƒt6ù¿†1ëGÀÁÊ5C“j{*&µi*þ5Ó¹²ëTf:QÀÛ4y*Æ*‚©{•Š¬6C3› ŽÌ˜Ë0°Àö‹¢Ðx@¶Ä*YA‡¾lÑY12«vðPus´0‰Ã%¤ÎªZY¾ ç¿.ß>ûÏû¾{÷ûŸŽ~ÿô÷ÿ2r|â•ØºÎ²îÕ€Æ¹IN[!õ“n#ÓÑ…œ„ScKhFÐDçÓÂ4¥‰ot–©Z•®•œÈvG¾OÀ<`Úž‰*&nB×~Mß±‘ÛEé$yëWøújˆóúúr_î$¿È¤ï*Ù1ZŒÊ§˜62BeÐa Ee4þÖ6§V•$¼à'qÌC’[I7µ1.³l|¯+á)ô/‡Î³ÀXãsˆW­„1V/”·V»”SlùNÅ!ÓjÕHZZæKYñXÿ”ù![ÓpbÜh¤Upê’UFµîTzÞ¤”NÏT¾X^°ûoªâfgô÷h4zîo×Ä1Ç Û¨5…‡~ô	O.ö÷žµ‰F46Î`MHÏ«ÆyïG7D"èøàˆèçíAÜÚççHý ‡Cg*jD¢dÈåýÀ¬iêDÙUaM©YÐwj"Ç§<ó:R]Í½Ò#ÛQEåŽåØÔÏž»#·•q¢–Ò)±ËÑŠß—÷9—’ŽÁ­ÊUSNì8\¿3+U}ú›˜ë<Aå“Õ¬ªg’œŽ"ÊÉ^ÝàÏ•žŒCuÊî à?„àHÊcH´“õDÅ©.ª\¯ÒWƒ©0Ž¦ªp©·…\ZW–ÂV”={>Z>L$h'#}ìÁ± 
í;«dÚió1™À™ål½•ª‡½ï™-ÆÒjÏÞUI ±àW“ã¤jêÌÃg:Ðk°6U Wj—‚CÕ_M ZüLcþ¥?‘çf.ˆx”6cÄp,<ù0Ö%¡—É^ÌßÀ‘zGâoø›&b_Ö"Áî€µ,‘Îˆ½Áþà}±7|A¸œ '}q9˜…*¶`Þ˜P¯©¾Š'“¾‹¾ç¹¤ë‚K¸­8¶SˆßB|5Ädïj¢êüÿPu©ò.G™ÉKS…«Òr}Lñs#sð _ïï½øJxýÃ·—½ph†cd±)Ò×pUÚ¦YMàXj@rü¸b…«×.N¾ø© •œ¼÷òœ¾PDÀ€($ˆFTö•P„èÈÑ~$$¥†Q·ãðÁ¢G)°ïåþ:öýÚ'·äÛÔ¿`)?HA Rý_F>	MP‡‚½uA~–TgâYœƒØ¥[¹ð´|[äðáùI”t`@çÆé‘¬e¡œªGâ=‘:“”àˆDßê²9	_\¸¯'‘ò4e®íèÞœ3ÅÒXzau,þBGÙcÐ_è¤6VÁ@S« §$zÓ&‰—{Ï¾Æ9AÖK_GÐ¤CëÇBç¼èIssãÏ9ÏD&Ê{éWGÔccŠcšû¯M¾%ÿ•äßÐóŠ,—±»GQpŸjIé„/à,}•jë&î|labì)ê ’ÖÉ§ÉiøÊ9w[îî= “·§ÖË—ÎqÖÏ'\sá=p$*„wvÉ¸ó",yJ?ë¢P©Æcça \~ò ?~	òƒôGÂïdÁíàpYw >•-Â3ø	Ö˜À4CâGf›±/_ÑS-!z Z[%öGË|"f-˜¾¿†ãlÜ[v_—ÝçÖUÞqÐ·›Dýsyå®.#ÜyNây Qã†³aÏ0~¹ÖXÏú¢j´jÅùQú@ë£PeÌFÛ‡ü=þ¬a‡ËªÒ%„m)r]Š\ä  K­†€éÚVÂvŒ[tÅñÕU¨Í¶Fý ŒúOÙ][¶_Ííî²¯eçXDôÔµ^lÿ-±f±G *g­Kç§ÚÏ•žL·ôº‡ðW^çýð˜óÜ»ðÚ‰Oæ€ts¥JàÃ)ï> …
6bE÷°|<à£Å9ïeÈ‚RN>Â¹ì½Ká¼…Î‰‹ÐâJ|BeOK;JM«>8önfy¤.à'Æ*#‘!™¤å’±LNc[öÕ_gu”Í&4Àèµ"ßˆKAsròø‚2JÔÝŸy~—¸YšÀïÕ~.cÄa=Ö®–~ogÜiî‡ððîÐ#†Ð›ÇïV•ÂÒÃrT–:¹ºÌ`Ãx@ß6QÃ¼ã
f¬¦r¦!®WŒÓ²€¿ÿŽ3£Q.Ë%€ã†6øò	1ÉŸ—Cµ)¢Ûðê]ÓØJ/’6ŠñWô¤åŸ%SGû¦Cæ²5WT êñyˆÛñù1›x'~Ž¥3všÍX^ç5å×ùÇ5}Þøfæ:â!Ò³yÙ*•V…[‚ÃæÜ
pÑ”= Ò‰Æ¿:‹ýp¯×å©E¡½ìQ¤>)
Ä=±SJ8Vm0P©ïg®}Ír\¨èÿ`$øè{E®â\`µLrë!“g œvÚ8¢N¼ºóÁÙÖ_j8á|.Ô¢E+¿trzr2òE2¿oÞ½?üéÛ}ËÖ|jòÞª_¸'µcÅã=W]LÆ+BbQ¦±(gªàÿ}oƒUí¯½‰ªZ¹¦ÆÓ6	Õ”çAR¤'¶Iã€fgI1©ÅÐŠ×ø‘šÑÞ¶£:ñKapaVœÎNÂŽÚãx‚~+Z—ÅQKûGâàEóÈË,oÉYÄÏš,µâ¿8³Ÿäm'ÆÖX›o8’oN—‚ç
¤=õ´]”IXx˜ùƒ€I˜pmêI}‹Í÷†®ð(ðÖ¹j¹½Ú—ªÜ¦ZcÃ1nº.“:çþ¼¸Ü øN*¨É9Ìhöö'«²&gE/)ëSð®)ª¨‘)&m¶Ìà˜‹-É@Æ`»,ëŠéxñèˆú0¹xè=¸÷©qüÐD|É¡Á+œÛ†hÐÞÁŠT*DòÕÆ["zrg­«¦Ô¼•5 öSßqÝséþß«ç³F,qãu kŒ§~ Ô-*næCñ÷ñ¤­‚e;ôþÛUiÊ!/ù¦üF4éUPç;Œý´Q$‡Û3}YD¾—Œ#£Æ	º®)Y¾ ‘e[F#1—5Sã@|ƒÚÅ­>¢¿õÒ Â3„áÅC;gL­~Öu5µ}tÓ
{Pörä;òØGóúò©®úy/p[¾-BŽ­)cÚO—¬ü+P¤§n²Ó”5 u®›„gÁð˜uè¹#ÏÛw´…tl?•=9]–pªTEkƒ¦Z´Ý§]~%/{—ãöú2NõŸhËªûaU¤õóì‰ÈpÄ÷¸šÞ7=:øføóµ·àTÚâ=¤{SiÃ3õ”ún:¨‹–’»£ËöÓE¤Ïñfnœw4Ž£ÃïCDjUâ÷MIÝY‡y~‘´°LÀMòÕûñÏ„âI¥®NEÏµƒùi„pñšð§ÊDV[–ÞKýìï÷î ½V>K£Žu¶Z«SÃ‰N©¤kÍb”z×øs)–fj*—iÞ£jè9á&Xn~0vp$UjèÃÅ|¿õ™“:~Ì‘î®^¬9ì^$ ?©µï#Rg*¡v·°¼Ós_Wy«‘xW×¦îÞ# 5¦ºn*¹°ôð¸z
¹2ÖmÑ»>z2óËã·Þ
kÐÔ[ènÁðV3Æx*÷fw£Ûâvs“»ØzX¬7·ÇÙíð€L-,KÜÄÌÚþ†u&¶ÅêfæuS¨:61+óøÊeUÝÂÞg•©6²©-P72©áÔ"p5P¼´r¼-Íî·4»¸ËÙÇm•v¯UÚ8^«NëÁ¿Eñ~
¶ÍŒñ³Lqá½Ônšáçá¶4¸Û2n3ãÛ 8èC½…íN+ºîZ°m‹»;.î65µÍ«†mwÇuÞÆv¶³(=||/öÜbó©vÊV2QCKû\ò–Xíë<´•„î6ƒ.”_±•I¢€IÝ”eÿuz)v"MïtåÂáR¿¨ïw³]]Œ]ëôíîkîg~; ðü>_ô»ïj5…v¼KÆNS¬£vµ¦Úi÷•éKó»¨úm pC•¸¶WÚ®¶¦t” ‰Öö½mùs§üy$ì¹w8ØòæNyÃ:Þˆ3—0%wÎ\È”s²ÚÛˆWòÅT[ºÜ1]LõÐÙ‘àêÀDíåÇõü>˜ÒÁþîK[³Qjx#=èÓÛ ~»w»ú.³L‡~!ÿêCíÚøP[×6F›¦úíX·øÞ'¾2aÀYÐ~ÇÃ—b—aŒ°UgIÞX=ShkWTKÚj»ÌÏµ¿ÐFkßóuÑŽº&ìJÉâ­ä©TâÝª|?ìTåa¬¨#r$ÞiÿŸD"W7@Õ._,ñÿ×Aï®aØÓ+¼²Î_ˆ¹äæîHTŽGsºêÔÚí·át~×`°˜“#~9~¬h`¤(G/ûàaøûHÜØàAý&Ÿ1mÝ»d~»ioìÃÏoß{Áž½K¼×²u£7ú?Ú u²k   0707010005c846000081b40000000000000000000000015ab2175400000eae000000660000000000000000000000000000003c00000000root/usr/share/doc/opensvc/template.service.fs.vfat.conf.gz   ‹      í]ksÛ6ýž_™LÆÎ¬¤µ³Íç1õn“Ì¶MgÝf?t:6D‚Ö$Á du»ÿ}Ï½ )P~HŽ±»ÒL[$/Á{Î}—ÈÓ§·ùyòTÜâ‡Äev Ü¢Rb–Iwsq·ø¸ÛÕÝ“Ÿ3ûtï—'¯øyßð?yB£>U‹¹©ÓƒåÍ“ZI§ŽMå´)-NÞâ‡ôdSÉq®º{þX7ŠÔê×F×j9˜÷2·|¤ªÍL[ŒG—“ƒø’Te²ÉÝrøß›’èrªjíd™„ûäJfâ­˜*™¶ƒPO®jÜV*Ñ™NpÎD•¸6¡ÓSÎTÉ·Ó\±æpo›àÀašjR•ÌEÐ™pFTÒò¿nªDn&:Áá™É›BCtQÈ2EÔ9Ò¿)ïD)qž¬ñ'ÇÉ©°Êá¼'¯úØ Èá#tzÒ˜ÆŠÅeÎ&ÿ×0F`ý8X¹&chRmOÅ¤6MÅ¿f:Wva*pÁL'
x›&OÅXE0u¡R‘Õ¦`hfÀ‘sØ~QÚÈ–X%+èÐ—-:+†CfÕª®qŽ¶f#q¡Ä‚ÔYU+Ëàü×åÛgÿyÿÏwï~ÿáÓÑïŸþþ_FŽO¼;páaB×YÖ½Ð87Éik ¤~Òmd:º“p`j¬Ã`	Íšè\`Z˜¦t#ñÎ2U«Òµ’YÂîÈ÷	˜'LÛ3"Q¥ÁÀDcÃMèÚ¯é;6r»($oý
__ño^__ãËä—™â]%;F‹CùÓFâCF¨Ú# ¨ŒÆßÚFàÔª’„ü$Žù`Hr+é¦¢6Æe–-ïu%<ƒþåÐy`k|ñª³£•0Æê…òÖj—rŠ-ÿÏ©8dZ­IKkÂ|)«ã/ë’2?d«qNBŒ´
N]²Êè±VÃ=‚JÏ›”Òé™ÊËvÿM5ÜìŒþFÏýàš8ædµ¦ðÐ>áÉÅþÞ³6ÑˆÆÆÙ ¬	éyÕ8ïýè†H=â<£=ˆ[ûüé¢äaãLEH”¹ø¢Ÿ ˜5M(»*¬)5úNMäxá”g^Gª«¹Wšcd;ª¨Ü±›úñÙswä¶2NÔR:"ö`9Zñûò>çRÒ±1¸U¹J`ÊAÀ‰‡ëwâc¥Ê£Osç"¨c²š€UõL’ÓÀQD9Ù«ü™¡òÃS‚q¨î@BÙ¤ü‡‰#Cy‰v²ž¨8õÑE•«áUúj05` ÆÑT.õ¶KëàÊRØŠ²gÏGË‡‰íd¤28@¡}g•L;m>&˜!³œ­·Rõ°ò=³ÅXZíÙ»*	$ö \BãjrœTÍCyøLzÖ¦
ôJíRp¨ú«	D‹ŸiÌ¿ôg òÜÌÒfŒŽ…'Æº$”à2Ù‹ù»8RïHüÓ$CìËºS$¸£Áp¢–%Òù±7Ø¼ /ö†/×€à¤/.³PÅÌ»ê5õÂBñdÒwÑ÷<—t]p	·µà‚Çv
ñ[ˆ¯†ø‚`ƒì]MTÝƒÿª.UÞå(3™#biªpU:@®¯“)~ndàëý½_	¯øö²n ÍpŒ,6EÚâ¡JÛÔ!«	KBŽ÷@¬à£põÚÅÉßƒ"¤’“÷^žÓŠE‚ÑˆÊþ Š9Ú$‚ƒ„ ´Á0êv>Xô(ö½Ü_Ç¾_ãä–|›ú, ã)Tª¿ãËHÂ'¡	êP°·.ÈÏ’êL<‹s»t+ ž–¯‘c‹2<!‰’èœÀ8=’µ,”SõH¼‡#Rg’œ‘è[]6g"á+ë÷áõ$Rž¦Ìu¡Ý›s¦XšëQ/¬ŽÅ_Hã(›búáñÔÆ*hjcDâ”„áAoÚ¤#ñrïÙ×8'Èzéëú‚thýXèœ½3"innü¹1ç™ÈDy/ýêˆzlLqLsÿµÉ·ä¿’üzÞC‘år"v÷(
îS-)ðœ¥¯RmýÏÄ#ÌCŒ=E@Ò:ù49-_9çnÁÝ½çdòöÔzùÒ9Îú¹à„k.¼ŽD…ðÎ.w^„e!ïCég]*Õxì<„ËOäÇï"A~ÞcãHø,¸.kãÔ§ ²Ex?Á˜fHüÈl3ö¥ó+zŠ %DD+`«Äþh™OÄ¬Ó÷×pœ{Ëîë²ûÜºÊ;úv“¨.¯ÜÕe„;ÏI<$jÜðc6ìÙ Æ2×zëâY_TV­8?Jh}ªŒ¹Áhû¿ÇŸ5ì°sYUº„°-E®K‘‹à “b©Õ0ýBÛJØŽq‹®x#¾º
µÙÖ¨„Qÿ‰ »kËö«¹ÝCöÕ ì+ˆžºöÃ‹í¿%Ö,öTå¬u	ãüT›ã¹Ò“é–^·âþÊë¼>âsž{^»#ñ‰À< n®T	|8åÝ@£PÁF¬è–|c´8ç½YPÊÉG8—½w)œ·Ð9qZ\‰O¨¬ãiiGI£iÕÇÞÍÌ"ÔüÄXeä!2$“´\2–ÉilË¾ú+ð¬Ž²Ù„æ½Väq)hNN_PF‰ºûó/Ïï7Kø½šÃÏÃe¬±‘8¬ÇÚÕ’ ÃïíŒ;ÍýÞz$ÂzóøÝªRXzXŽ
ÒÂRB'W—lèÛ&j˜w\ÁŒÕTÎ4ÄõŠqZð÷ßrf4ÊeY¢pÜÐ_>!&ù3âr¨6Et^] k»CéEÒF1þŠž´ü³dªñhßtÈ\¶æŠ
D=>q;>?fïÄÏ±tÆN³Ëë¼¦üš"ÿ¸¦/Àÿ£ÓìÃ\G<Dz6/[¥ÒªpRpØœ[.š²T:ÑøWg±î•àº<µ(´—=ŠÔ'E¸Ç 2vJ	Çª*õýÁµ¯YŽýŒ_}¯ÈUÜ¬–	An=dòT€ÓNGÔ‰Ww>8ÛúK'œÏå‚Z´hå‚NNàAONF¾¨Cæ÷Í»÷‡?}û£oÙšOMÞ[õ÷„ vŒ£x¼çª‹©ÁxEH,Ê4åLü¿ïm°ªýµ7QU+×ÔxÚ&¡šã<HŠôàÄ6iÐì,)&µZñ?R3ÚÛvT'~).ÌŠƒÓùÁIxÂQ{OÐoEkcãò±8jiÿH¼hy9åí#9ËøY³“¥Vüçaö“¼íÄØkóGòñÍé²Qð\´§ž¶‹2	kÓ!ÿ`0	®Mý"©oñ ùÞÐþÂ:W-·WûR•ÛTklx ÆM—ÃeRç\ÃŸ—ß	B59‡ÍÞþdUÖä¬è%¥s}
Þ5E52Å¤Í–s±%È¸l—e]@1/Q&½÷>5Žßšˆ/94x…sÛÚ;X‘J…H¾ÚxkADOî¬uÕ”š·2¢´Ã~ªà;®{.Ý_â{õ|Öˆ‚%n¼ä`ñÔ„ºEÅÍ|(^âþ ž4¢UC°l‡ÞŸ`»*M9ä%ß”ßÈ€&½

ã|‡±Ÿ6Šäp{¦/‹È÷’qdÔ8A×5% Ë4²ŒbËh$æ²fjˆoP»¸ÕGô·^Txæƒ0¼xhçŒ©ÕÏº®¦–¡Î`ZaÊ^Ž|Gûh^_>ÕU?ïnË·EÈ±5eŒAûé’•ŠôTÃMvš²¤Îu“ð,³=wäyûŽ¶ŽMà§²'§ËN•ªhmÐT‹¶û´Ë¯¤óeïrÜÞA_Æ©þmYu?¬Š´~ž=nÂ€øWsÀû¦GÿÃŒÂ¾öœJ[¼‡to*mx¡s`† žRßBuÑRrwÔbÙ~ºˆ4ð9ÞlÂóŽÆqtø}ˆHí¢Jü^¢)©;ë0Ï/’–	¸Iž£z?þyPœ!©ÔÁ©è¹v0?.^þT™ÈjËÒ»`©Ÿ=ðýÞ„¡×ÊgiÔq¡ÎVkuj8Ñ)µ€t­YŒRïŸc.ÅÒLmcAå2Í{T='¼ÁËÍÂ¯ÆŽ¤J}ø¢˜ï·>sRÇ9ÒÝÕ‹5‡Ý‹”á'µö}DêL%Ôî–wzîë*o5ïêÚÔÝ{´ÆT×Må"²“WO!WÆº-z×GOf~yüÖÁ[Ácz€zÝ-ÞjÆOå^Ãì®ct[Ünnr·[‹õæö8»©…e‰›˜YÛß°ÎÄ¶XÝÌ¼n
U‡Á&fe_ùñ ¬ª[Øûl£2ÕF6µêF&u#œZ®Š7€VŽ·¥Ùý–f—w"›â¸­ÒîµJÛ ÇkÕi=ø·(ÞOÁ¶™1~–)n!¼—ÚmC3ü#Ü–w[Æmf|}¨·°ÝiE·¡Á]¶mqwÇÅÝ¦¦¶yÕ°­óî¸ÎÛØÎÖae ÇAƒïÅž»Al>ÕNÙJ&jhiŸKÞ«}‡¶’ÐÝfÐ…ò+¶2I0©›²ì¿N/ÅN¤é®\8\
âõýn¶«‹ñ¯k¾Ý}ÍýÌo´žßç‹~÷]­¦ÐŽwÉØiªuÔ®ÖT;í¾2}i~U¿ n¨×öJÛÕÖ”Ž ÑÚ¾·-î”?„=×âç[ÞÜ)oXÇqæ¦Dâ®Ã™™rNVÛaÑàJ¾˜jK—;¦‹©:["\˜¨½ü¸žßS:ØÿÀ}ik6Jo¤½3¢czÀo÷nWße–éÐo ä_}¨]?jëÚÆhÓT¿ëßûÄW&¬ 8ÚïxøRì2Œ¶ê,É«gj mMàŠjI[m—ùâ9£öÚhÍâ{Þ£.ÚQ×„]©"Y¼•<•J¼[•ï‡ª<ìuDŽÄ»3íÿ3‚Häê¨a£Úå‹%þÿ:è½Ñ5{z…WÖù1—ÜÜ‰ÊñhN2PZ»ý6œÎïsrÄ/ÇŒåèe<‰<¨ßä3â¢­{—Ìo7í}øùí{/Ø³w)bƒ÷Z¶nôáFÿ_¼{Ð°k    0707010005c847000081b40000000000000000000000015ab2175400000eae000000660000000000000000000000000000003c00000000root/usr/share/doc/opensvc/template.service.fs.vxfs.conf.gz   ‹      í]ksÛ6ýž_™LÆÎ¬¤µ³Íç1õn“Ì¶MgÝf?t:6D‚Ö$Á du»ÿ}Ï½ )P~HŽ±»ÒL[$/Á{Î}—ÈÓ§·ùyòTÜâ‡Äev Ü¢Rbv–Ù›‹»ÅÄÝ®îžüœÙ§{¿<yÅÏû†ŸøÉõ©ZÌM,ožÔJ:ul*§MiqÊð?¤'›˜JŽsÕÝóÇºQt V¿6ºVËÁ¼—¹å#UmfÚb<ºœÄ—¤*“Mî–ÃÿÞ”|@—SUk'Ë$Ü'W2oÅTÉ´„xrUûã¶R‰Ît‚s&ªÄµ	–˜r¦êH¾æêŒ5‡{ÛÓT“ªd.‚Î„3¢’–ÿuS%r3Ñ	ÏLÞ*(¢‹B–é(¢Î‘þM	|'J‰ód?9NN…Uç=yÕÇ@‡¡Ó“Æ4V,.ƒt6ù¿†1ëGÀÁÊ5C“j{*&µi*þ5Ó¹²ëTf:QÀÛ4y*Æ*‚©{•Š¬6C3› ŽÌ˜Ë0°Àö‹¢Ðx@¶Ä*YA‡¾lÑY12«vðPus´0‰Ã%¤ÎªZY¾ ç¿.ß>ûÏû¾{÷ûŸŽ~ÿô÷ÿ2r|â•ØºÎ²îÕ€Æ¹IN[!õ“n#ÓÑ…œ„ScKhFÐDçÓÂ4¥‰ot–©Z•®•œÈvG¾OÀ<`Úž‰*&nB×~Mß±‘ÛEé$yëWøújˆóúúr_î$¿È¤ï*Ù1ZŒÊ§˜62BeÐa Ee4þÖ6§V•$¼à'qÌC’[I7µ1.³l|¯+á)ô/‡Î³ÀXãsˆW­„1V/”·V»”SlùNÅ!ÓjÕHZZæKYñXÿ”ù![ÓpbÜh¤Upê’UFµîTzÞ¤”NÏT¾X^°ûoªá¨˜ˆÑhôÜß®‰cŽA¶Qk
ýèž\ìï=khlœÀšžWóÞnˆDÐñÁÑ#Î3Úƒ¸µÏÏ‘.úA†0ÎTÔˆDÉ‹/ú	€YÓÔ‰²«ÂšR³ ïÔDŽNyæu¤ºš{¥9F¶£ŠÊË±©Ÿ=wGn+ãD-¥S b–£¿/ïs.%ƒ[•«¦œØq¸~g >Vª<úô71×y.‚Ê1&«	XUÏ$9E”“½ºÁŸ*?<%‡ê$”ÝA:ÀÁ‘82”Çh'ë‰ŠS]T¹*^¥¯S`MUáRo¹´®,…­({ö|´|˜HÐNFúØ!ƒcÚwVÉ´Óæc22ËÙz+U{!ß3[Œ¥Õž½«’@bÀ%4®&ÇIÕ<Ô™‡Ït ×`mª@¯Ô.‡ª¿š@´ø™ÆüK"ÏÍ\ñ(mÆˆáXxòa¬KB	.“½˜¿€#õŽÄßð7M2Ä¾¬;E‚;Ü'jY"?{ƒýÁúboø‚p8Núâr0UlÁ¼;0¡^S/|!O&}}ÏsI×—p[.pl§¿…øjˆ/6ÈÞÕDÕ=øÿ¡êRå]Ž2“9"–¦
W¥äú:™âçFæà¾Þß{ñ•ðú‡o/{áÐÇÈbS¤-®á 1ª´M²šÀ±Ô€ äøqÄ
>
W¯]œ|ñ=(RA*9yïå9}¡ˆ€Q$H¨ì*¡Ñ‘£ýH"8HJ£nÇáƒER`ßËýuìûµ1NnÉ·©!ÀR0~‚@¥ú;1¾Œ$|š {ë‚ü,©ÎÄ³8±K·ráiù9¶Èá!Ãó’(éÀ€Î	ŒÓ#YËB9UÄ{8"u&)Á‰¾Ões&¾¹þp^O"åiÊ\ÚÑ½9gŠ¥°õÂêXü…4Ž²)Æ ¿ÐIm¬‚¦6VA$NIô¦M:/÷ž}s‚¬—¾Ž /H‡Ö…ÎyÑ;#’ææÆŸsž‰L”÷Ò¯Ž¨ÇÆÇ4÷_›|Kþ+É¿¡ç=Y.'bw¢à>Õ’Ò	_ÀYú*ÕÖÿLÜùØ8Â<ÄØSÔ$­“O“Ó2ð•sî¶ÜÝ{>@&oO­—/ã¬ŸN¸æÂ{àHTïì’qçEXò>”~ÖE¡RÇÎÃ@¸üäA~ü.äé=6Ž„ßÉ‚ÛÁá²6î@}
*[„gð¬1i†ÄÌ6c_:¿¢§ZBô@´¶Jì–ùDÌZ0}ÇÙ¸·ì¾.»Ï­«¼ã o7‰úçòÊ]]F¸óœÄó@¢Æ?fÃž`ü s­'°.žõEÕhÕŠó£ôÖG¡Ê˜Œ¶ù{üYÃ;—U¥KÛRäº¹È:)–ZÓ/´­„í·èŠ7â««P›múAõŸ²»¶l¿šÛÝ9d_ÊÎ±‰è©k?¼Øþ[bÍb@UÎZ—0ÎOµ9ž+=™néu+á¯¼Îûá#1ç¹wáµ;ŸÌ éæJ•À‡SÞ} 4
lÄŠîaùxÀ7F‹sÞË¥œ|„sÙ{—Ây¡Å•ø„Ê:ž–v”4šV}pìÝÌ,òH]ÀOŒUF"C2IË%c™œÆ¶ì«¿Ïê(›Mhþ€ÑkE¾—‚æääñe”¨»?ÿòü.q³4ß«9ü<\Æ‰Ãz¬]-	:üÞÎ¸ÓÜááÝ¡G"¡7ß­*…¥‡å¨ -,%tru™Á†ñ€¾m¢†yÇÌXMåLC\¯§eÿ!gF£\–%J Çmðåb’?#.‡jSD·áÕº¦±;”^$mã¯èIË?K¦öM‡Ìek®¨@Ôãó·ãóc6!ðNüKgì4›±¼ÎkÊ¯)òkú¼ñ?:Í>ÌuÄC¤gó²U*­
· ‡Í¹à¢){@¥uûá^	®ËS‹B{Ù£H}Rˆ{ c§”p¬Ú` RßÏ\ûšå¸PÑÿÁHð%Ð÷Š\Å¸Àj™äÖC&Ï@8í´qDxuçƒ³­¿ÔpÂù\.¨E‹V~!èäôädä‹:d~ß¼{øÓ·?ú–­ùÔä½U¿pOjÇ8ŠÇ{®º˜ŒW„Ä¢LcQÎTÁÿûÞ«Ú_{UµrM§mª)1Îƒ¤HNl“ÆÍÎ’bR‹¡¯ñ#5£½mGuâ—ÂàÂ¬88œ„'µÇñýV´66.‹£–öÄÁ‹æ‘—YÞ>’³ˆŸ5;YjÅqf?ÉÛNŒ­±6ßp$ßœ.ÏH{êi»(“°&ð0ò“0áÚÔ/’úšï]àQà/¬sÕr{µ/U¹MµÆ†bÜt9\&uÎ5üyq¹A0ð TP“s˜ÑìíOVeMÎŠ^R:×§à]STQ#SLÚl™Á1[’ŒÀvYÖÓñâÑõarñÐ{pïSãø= ‰ø’CƒW8·Ñ ½ƒ©Tˆä«·DôäÎZWM©y+#j@;ì§
¾ãºçÒý%¾WÏg(XâÆë@ÖOý@¨[TÜÌ‡â%îâI#Z5Ëvèý	¶«Ò”C^òMùhÒ« 0Îwûi£H·gú²ˆ|/GFt]S²|A#Ë(¶ŒFb.k¦Æøµ‹[}Dë¥A…g>Ã‹‡vÎ˜Zý¬ëjjúè¦ö ìåÈwä±æõåS]õó^à¶|[„[SÆ´Ÿ.YùW HO5Üd§)k@ê\7	Ï‚á1ëÐsGž·ïhéØ~*{rº,áT©ŠÖMµh»O»üJ:_ö.Çíôeœê?Ñ–U÷ÃªHëçÙ‘á&ˆïq5¼oztð?Ì(ðçkoÁ©´Å{H÷¦Ò†:fê)õÝ tP-%wG-–í§‹HŸãÍ&Ü8ïhG‡ß‡ˆÔ.ªÄï%š’º³óü"ia™€›ä9ª÷ãŸÅ’J]œŠžkóÓáâ5áO•‰¬¶,½–úÙßïÝAz­|–FêlµV§†RH×šÅ(õ®ñ9æR,ÍÔ6T.Ó¼GÕÐsÂ+L°Ü,ü:aìàHªÔÐ‡/Šù~ë3'uü˜#Ý]½XsØ½H@~RkßG¤ÎTBínay§ç¾®òV#ñ®®MÝ½G@kLuÝT.ra!;éáqõre¬Û¢w}ôdæ—Ço¼<Ö ¨·ÐÝ‚á­fŒñTî5Ìî:F·Åíæ&w°õ°Xon³Ûá™ZX–¸‰™µýëLl‹ÕÍÌë¦PulbVæñ•Êªº…½Ï6*SmdS[ ndR7Â©Eàj xhåx[šÝoiv)p—!²)ŽÛ*í^«´p¼VÖƒ‹âýl›ãg™âÂ{©Ý64ÃÏ1Âmip·eÜfÆ·AqÐ‡zÛVtÜµ`Ûww\Ümjj›WÛ:ïŽë¼ílfQz4øø^ì¹ÄæSí”­d¢†–ö¹ä-±Ú×yh+	Ým](¿b+“D“º)ËþëôRìDšÞéÊ…Ã¥ ~Qßïf»ºÿºÖéÛÝ×ÜÏüv@áù}¾èwßÕj
íx—Œ¦XGíjMµÓî+Ó—æwQõÛ à†*qm¯´]mMé(­í{ÛòçNùóHØs-îp°åÍò†u¼g.aJ$î:œ¹)çdµ¶®ä‹©¶t¹cº˜ê¡³%"ÁÕ‰ÚËëù}0¥ƒýÜ—¶f£ÔðFzÐ;#:¦·üvïvõ]f™ýBþÕ‡Úµñ# ¶®mŒ6MõÛ±nñ½O|eÂ
€³ ýŽ‡/Å.Ãa«Î’¼±z¦ÐÖ®¨–´Õv™/ž3j¡Ö,¾ç=ê¢uMØ•*’Å[ÉS©Ä»Uù~Ø©ÊÃXQGäH¼;Óþ?#ˆD®n€6ª]¾Xâÿ¯ƒÞ]Ã°§Wxe¿sÉÍÝ‘¨æt!Õ©µÛoÃéü®Á`1'GürüXÑÀHQŽ^öÁÃð÷‘¸±ÁƒúM>c .ÚºwÉüvÓÞØ‡Ÿß¾÷‚={—"6x¯eëFÿnôóÀz}°k    0707010005c848000081b40000000000000000000000015ab2175400000eae000000660000000000000000000000000000003b00000000root/usr/share/doc/opensvc/template.service.fs.xfs.conf.gz    ‹      í]ksÛ6ýž_™LÆÎ¬¤µ³Íç1õn“Ì¶MgÝf?t:6D‚Ö$Á du»ÿ}Ï½ )P~HŽ±»ÒL[$/Á{Î}—ÈÓ§·ùyòTÜâ‡Äev Ü¢Râ,³· î?w»º{òsfŸîýòä?ïzâ'OhÐ§j17uz°¼wR+éÔ±©œ6¥Å)Ã[üšlb*9ÎUwÏëFÑZýÚèZ-ó^æ–Tµ™i‹ñèrr_’ªL6¹[ÿ{Sò]NU­,“pŸ\ÉL¼S%ÓvJàÉUíÛJ%:Ó	Î™¨×&tZbÊ™ª#ùvš«3Öîm8LSMª’¹:ÎˆJZþ×M•ÈÍD'8<3yS¨ `ˆ.
Y¦£ˆ9Gú7%ð(%Î“5þä89V9œ÷äUà8b„NOÓX±¸ÒÙäÿÆ¬+×dMªí©˜Ô¦©ø×LçÊ.¬S.˜éDoÓä©«¦î1T*²ÚÍl82c.ÃÀÛ/ŠB{àÙ«dú²EgÅpÈ¬ÚÁCÕ5ÎÑ6Âl$#”X:«jeùœÿº|ûì?ïÿùîÝï?|:úýÓßÿËÈñ‰Wb.<Lè:ËºWç&9m„ÔOºLGrLu,¡ALÓ”n$¾ÑY¦jUºVr"KØù>ó‚i{F$ª4˜hl¸	]û5}ÇFn¥“ä­_áë«!þÍëëËa|¹“ü2 “B¼«dÇh1b(ŸbÚH|È•A{„•Ñø[ÛœZU’ð‚ŸÄ1In%ÝTÔÆ¸Ì²ò½®„§`Ð¿:ÌcÏ!^uv´ÆX½PÞZíRN±åÿ9‡L«U#iiM˜/euüÅcýCRæ‡l5NÃIˆq£‘VÁ©KV=Öj¸GPéy“R:=SùbyÁî¿©„›Ñß£Ñè¹¿\Çƒl£ÖúÑ'<¹Øß{Ö&ÑØ8€5!=¯ç½Ý‰ ãƒ#¢Gœg´qkŸŸ#]ôƒ"aœ©¨‰’!×^ô ³¦©eW…5¥fAß©‰/œòÌëHu5÷JsŒlG•;–cS?>{îŽÜVÆ‰ZJ§@Ä,G+~_Þç\J:6·*W	L98±ãpýÎ@|¬Tyôéob®ó\•cLV°ªžIr8Š('{uƒ?3T~xJ0ÕH(»ƒt€ÿ‚#qd(!ÑNÖ§>º¨rU ¼J_¦À8šªÂ¥Þri\Y
[Qöìùhù0‘ Œô±CÇ(´ï¬’i§ÍÇd3d–³õVªöB¾g¶K«={W%Ä€Kh\MŽ“ªy¨3Ÿé@¯ÁÚT^©]
U5hñ3ù—þDž›¹ âQÚŒÃ±ðäÃX—„\&{17 Gê‰¿áošdˆ}YwŠw4¸NÔ²D: öûƒôÅÞðápœôÅå`ªØ‚yw`B½¦^øB(žLú.úžç’®.á¶\0àØN!~ñÕ_l½«‰ª{ðÿCÕ¥Ê»e&sD,M®JÈõu2ÅÏÌÁ|½¿÷â+áõß^öÂ Ž‘Å¦H[\ÃAb Ti›:d5c©AÈñãˆ|®^»8ùâ{P¤‚TròÞËsúB¢H QÙTB¢#Gû‘Dp”6FÝŽÃ‹¥À¾—ûëØ÷kcœÜ’oSÿB€¤`ü Jõwb|Iø$4A
öÖùYR‰gqb—näÂÓò5rl‘ÃC†ç/$QÒ§G²–…rª‰÷pDêLR‚3 }«ËæL$|rýá>¼žDÊÓ”¹.´£{sÎK3`=ê…Õ±øieSŒA?<~¡“ÚXMm¬‚Hœ’0<èM›t$^î=ûçY/}A_­ó¢wF$ÍÍ?7æ<™(ï¥_Q)Žiî¿6ù–üW’CÏ{(²\NÄîEÁ}ª%¥¾€³ôUª­ÿ™¸ó±q„yˆ±§¨HZ'Ÿ&§eà+çÜm!¸»÷|€LÞžZ/_:ÇY?œpÍ…÷À‘¨ÞÙ%ãÎ‹°,ä}(ý¬‹B¥‡pùÉƒüø]$ÈÒ{l	¿“·ƒÃemÜúT¶Ïà'XcÓ‰™mÆ¾t~EO´„èhl•Ø-ó‰˜µ`úþŽ³qoÙ}]vŸ[WyÇAßnõÏå•»ºŒpç9‰çD~Ì†=ÀøAæZO`]<ë‹ªÑªçGé­B•17mò÷ø³†v.«J—¶¥Èu)r‘<tR,µ¦_h[	Û1nÑoÄWW¡6Ûõƒ0ê?dwmÙ~5·»sÈ¾”cÑS×~x±ý·ÄšÅªœµ.aœŸjs<Wz2ÝÒëVÂ_y÷ÃG<bÎsïÂkw$>˜ ÒÍ•*§¼û h*ØˆÝÃòñ€oŒç¼—!J9ùç²÷.…ó:'.B‹+ñ	•u<-í(i4­úàØ»™Yä‘º€Ÿ«Œ<D†d’–KÆ29mÙWžÕQ6›Ðü£×Š|#.ÍÉÉãÊ(Qwþåù]âfi¿Wsøy¸Œ56‡õX»Ztø½q§¹ÂÃ»CDBo¿[U
KËQAZXJèäê2ƒã}ÛDóŽ+˜±šÊ™†¸^1NËþþ;BÎŒF¹,K” ŽÚàË'Ä$F\Õ¦ˆnÃ«tMcw(½HÚ(Æ_Ñ“––L5í›™ËÖ\Q¨Çç!nÇçÇlBàø9–ÎØi6cy×”_Sä×ôxãtš}˜ëˆ‡HÏæe«TZnA
›s+ÀESö€J'ÿê,öÃ½\—§…ö²G‘ú¤(÷@ÆN)áXµÁ@¥¾Ÿ!¸ö5Ëq¡¢ÿƒ‘àK ï¹Š;pÕ2!È­‡Lž
pÚiãˆ:ñêÎg[©á„ó¹\P‹­üBÐÉ	<èÉÉÈuÈü¾y÷þð§oô-[ó©É{«~ážÔŽq÷\u15¯‰E™Æ¢œ©‚ÿ÷½Vµ¿ö&ªjåšOÛ$TSbœI‘œØ&š%Å¤C+^ãGjF{ÛŽêÄ/…Á…Yqp:?8	O8jã	ú­hml\>G-í‰ƒÍ#/²¼}$g9?kv²ÔŠÿâ<Ì~’·[cm¾áH>¾9]6
ž+öÔÓvQ&aMàa:ä&aÂµ©_$õ-4ßº"À£À_Xçªåöj_ªr›jÄ¸ér¸Lêœkøóârƒ`à;A¨ &ç0£ÙÛŸ¬Êšœ½¤t®OÁ»¦¨¢F¦˜´Ù2ƒc.¶$7€í²¬(¦ãÅ£#êÃäâ¡÷àÞ§Æñ{@ñ%‡¯pn¢A{+R©ÉWo-ˆèÉµ®šRóVFÔ€vØO|ÇuÏ¥ûK|¯žÏQ°Ä×¬1žúP·¨¸™ÅKÜÄ“F´j–íÐûlW¥)‡¼ä›òÐ¤WAaœï0öÓF‘nÏôeù^2ŽŒ'èº¦dù‚F–QlÄ\ÖLñj·úˆþÖKƒ
Ï|†íœ1µúY×ÕÔ2ôÑL+ìAÙË‘ïÈcÍëË§ºêç½Àmù¶9¶¦Œ1h?]²ò¯@‘žj¸ÉNSÖ€Ô¹nžÃcÖ¡çŽ<oßÑÒ±	üTöätYÂ©R­šjÑvŸvù•t¾ì]ŽÛ;èË8Õ¢-«î‡U‘ÖÏ³'"ÃMßãjxßôèà˜Q8àÏ×Þ‚Si‹÷îM¥/t.ÌÔSê»Aè .ZJîŽZ,ÛO‘>Ç›M¸qÞÑ8Ž¿©]T‰ßK4%ugæùEÒÂ27ÉsTïÇ?/Š3$•º"8=×æ§ÂÅkÂŸ*YmYz,õ³¾ß»ƒ0ôZù,:.ÔÙj­N':¥®5‹Qê]ãsÌ¥Xš©m,¨\¦yª¡ç„W ˜`¹YøuÂØÁ‘T©¡_óýÖgNêø1Gº»z±æ°{‘€2ü¤Ö¾H©„ÚÝÂòNÏ}]å­Fâ]]›º{€Ö˜êº©\äÂBvÒÃãê)äÊX·EïúèÉÌ/ß:x+x¬APo¡»Ã[Íã©Ük˜ÝuŒn‹ÛÍMî6`ëa±ÞÜg·Ã2µ°,q3kûÖ™Ø«›™×M¡ê0ØÄ¬Ìã+?”Uu{ŸmT¦ÚÈ¦¶@ÝÈ¤n„S‹ÀÕ@ñ0ÐÊñ¶4»ßÒìRà.CdS·UÚ½Viàx­:­ÿÅû)Ø63ÆÏ2Å-„÷R»mh†Ÿc„ÛÒànË¸ÍŒoƒâ õ¶;­è64¸kÁ¶-îî¸¸ÛÔÔ6¯¶uÞ×yÛÙ:Ì¢ô8hðñ½Øs7ˆÍ§Ú)[ÉD-ísÉ[bµ¯óÐVºÛºP~ÅV&‰&uS–ý×é¥Ø‰4½Ó•‡KAü¢¾ßÍvu1þu­Ó·»¯¹Ÿùí€6Âóû|Ñï¾«ÕÚñ.;M5°ŽÚÕšj§ÝW¦/Íï¢ê·ÀUâÚ^i»ÚšÒQ$ZÛ÷¶åÏòç‘°çZÜá<`Ë›;åëx#Î\Â”HÜu8s!SÎÉj;l#\ÉSmérÇt1ÕCgKD‚«µ—×óû`Jû¸/mÍF©áô wFtLoøíÞíê»Ì2ú„ü«µkãG@m]Ûmšê·cÝâ{ŸøÊ„ gAû_Š]†1ÂV%ycõL ­	\Q-i«í2_<gÔþB­Y|Ï{ÔE;êš°+U$‹·’§R‰w«òý°S•‡=°¢ŽÈ‘xw¦ýF‰\Ý 5lT»|±Äÿ_½7º†aO¯ðÊ:!æ’›»#Q9ÍéBªSk·ß†Óù]ƒÁbNŽøåø±¢‘¢½ìƒ‡áï#qcƒõ›|Æ@\´uï’ùí¦½±?¿}ï{ö.Elð^ËÖþ!Üèÿ §mœ¯k    0707010005c849000081b40000000000000000000000015ab2175400000ead000000660000000000000000000000000000003b00000000root/usr/share/doc/opensvc/template.service.fs.xia.conf.gz    ‹      í]ksÛ6ýž_™LÆÎ¬¤µ³Íç1õn“Ì¶MgÝf?t:6D‚Ö$Á du»ÿ}Ï½ )P~HŽ±»ÒL[$/Á{Î}—ÈÓ§·ùyòTÜâ‡Äev Ü¢RâLË[w‹ˆ»]Ý=ù9³O÷~yòŠŸ÷=ñ“'4èSµ˜›:=XÞ;©•têØTN›Òâ”á-~HM61•çª»çu£è@­~mt­–ƒy/sËGªÚÌ´Åxt99ˆ/IU&›Ü-‡ÿ½)ù€.§ªÖN–I¸O®d&ÞŠ©’i;%ðäªöÇm¥éçLT‰k:-1åLÕ‘|;ÍÕk÷¶	¦©&UÉ\	gD%-ÿë¦Jäf¢ž™¼)TP0D…,ÓQÄœ#ý›øN”çÉrœœ
«Î{òªp1B§'i¬X\élòcÖ€ƒ•k2†&ÕöTLjÓTük¦seÖ©Ìt¢€·iòTŒUS÷*Ym
†f6™1—a`íE¡=ð€l‰U²‚}Ù¢³b8dVíà¡êçha6‡J,HUµ²|Î]¾}öŸ÷ÿ|÷î÷>ýþéïÿeäøÄ+±&teÝ«s“œ¶Bê'ÝF¦£9	¦Æ:–ÐŒ ‰Î¦…iJ7ßè,Sµ*]+9‘%ìŽ|Ÿ€yÁ´=#UL46Ü„®ýš¾c#·‹ÒIòÖ¯ðõÕÿæõõå0¾ÜI~I!ÞU²c´1”O1m$>d„Ê =Â ŠÊhü­mN­*IxÁOâ˜†$·’n*jc\fÙù^WÂS0è_f±Æç¯:;Z	c¬^(o­v)§ØòÿœŠC¦Õª‘´´&Ì—²:þâ±þ!)óC¶§á$Ä¸ÑH«àÔ%«Œk5Ü#¨ô¼I)ž©|±¼`÷ßTÂÍÎèïÑhôÜß®‰cŽA¶Qk
ýèž\ìï=khlœÀšžWóÞnˆDÐñÁÑ#Î3Úƒ¸µÏÏ‘.úA†0ÎTÔˆDÉk/ú	€YÓÔ‰²«ÂšR³ ïÔDŽNyæu¤ºš{¥9F¶£ŠÊË±©Ÿ=wGn+ãD-¥S b–£¿/ïs.%ƒ[•«¦œØq¸~g >Vª<úô71×y.‚Ê1&«	XUÏ$9E”“½ºÁŸ*?<%‡ê$”ÝA:ÀÁ‘82”Çh'ë‰ŠS]T¹*^¥¯S`MUáRo¹´®,…­({ö|´|˜HÐNFúØ!ƒcÚwVÉ´Óæc22ËÙz+U{!ß3[Œ¥Õž½«’@bÀ%4®&ÇIÕ<Ô™‡Ït ×`mª@¯Ô.‡ª¿š@´ø™ÆüK"ÏÍ\ñ(mÆˆáXxòa¬KB	.“½˜¿€#õŽÄßð7M2Ä¾¬;E‚;Ü'jY"?{ƒýÁúboø‚p8Núâr0UlÁ¼;0¡^S/|!O&}}ÏsI×—p[.pl§¿…øjˆ/6ÈÞÕDÕ=øÿ¡êRå]Ž2“9"–¦
W¥äú:™âçFæà¾Þß{ñ•ðú‡o/{áÐÇÈbS¤-®á 1ª´M²šÀ±Ô€ äøqÄ
>
W¯]œ|ñ=(RA*9yïå9}¡ˆ€Q$H¨ì*¡Ñ‘£ýH"8HJ£nÇáƒER`ßËýuìûµ1NnÉ·©!ÀR0~‚@¥ú;1¾Œ$|š {ë‚ü,©ÎÄ³8±K·ráiù9¶Èá!Ãó’(éÀ€Î	ŒÓ#YËB9UÄ{8"u&)Á‰¾Ões&¾¹þp^O"åiÊ\ÚÑ½9gŠ¥°õÂêXü…4Ž²)Æ ¿ÐIm¬‚¦6VA$NIô¦M:/÷ž}s‚¬—¾Ž /H‡Ö…ÎyÑ;#’ææÆŸsž‰L”÷Ò¯Ž¨ÇÆÇ4÷_›|Kþ+É¿¡ç=Y.'bw¢à>Õ’Ò	_ÀYú*ÕÖÿLÜùØ8Â<ÄØSÔ$­“O“Ó2ð•sî¶ÜÝ{>@&oO­—/ã¬ŸN¸æÂ{àHTïì’qçEXò>”~ÖE¡RÇÎÃ@¸üäA~ü.äé=6Ž„ßÉ‚ÛÁá²6î@}
*[„gð¬1i†ÄÌ6c_:¿¢§ZBô@´¶Jì–ùDÌZ0}ÇÙ¸·ì¾.»Ï­«¼ã o7‰úçòÊ]]F¸óœÄó@¢Æ?fÃž`ü s­'°.žõEÕhÕŠó£ôÖG¡Ê˜Œ¶ù{üYÃ;—U¥KÛRäº¹È:)–ZÓ/´­„í·èŠ7â««P›múAõŸ²»¶l¿šÛÝ9d_ÊÎ±‰è©k?¼Øþ[bÍb@UÎZ—0ÎOµ9ž+=™néu+á¯¼Îûá#1ç¹wáµ;ŸÌ éæJ•À‡SÞ} 4
lÄŠîaùxÀ7F‹sÞË¥œ|„sÙ{—Ây¡Å•ø„Ê:ž–v”4šV}pìÝÌ,òH]ÀOŒUF"C2IË%c™œÆ¶ì«¿Ïê(›Mhþ€ÑkE¾—‚æääñe”¨»?ÿòü.q³4ß«9ü<\Æ‰Ãz¬]-	:üÞÎ¸ÓÜááÝ¡G"¡7ß­*…¥‡å¨ -,%tru™Á†ñ€¾m¢†yÇÌXMåLC\¯§eÿ!gF£\–%J Çmðåb’?#.‡jSD·áÕº¦±;”^$mã¯èIË?K¦öM‡Ìek®¨@Ôãó·ãóc6!ðNüKgì4›±¼ÎkÊ¯)òkú¼ñ?:Í>ÌuÄC¤gó²U*­
· ‡Í¹à¢){@¥uûá^	®ËS‹B{Ù£H}Rˆ{ c§”p¬Ú` RßÏ\ûšå¸PÑÿÁHð%Ð÷Š\Å¸Àj™äÖC&Ï@8í´qDxuçƒ³­¿ÔpÂù\.¨E‹V~!èäôädä‹:d~ß¼{øÓ·?ú–­ùÔä½U¿pOjÇ8ŠÇ{®º˜ŒW„Ä¢LcQÎTÁÿûÞ«Ú_{UµrM§mª)1Îƒ¤HNl“ÆÍÎ’bR‹¡¯ñ#5£½mGuâ—ÂàÂ¬88œ„'µÇñýV´66.‹£–öÄÁ‹æ‘—YÞ>’³ˆŸ5;YjÅqf?ÉÛNŒ­±6ßp$ßœ.ÏH{êi»(“°&ð0ò“0áÚÔ/’úšï]àQà/¬sÕr{µ/U¹MµÆ†bÜt9\&uÎ5üyq¹A0ð TP“s˜ÑìíOVeMÎŠ^R:×§à]STQ#SLÚl™Á1[’ŒÀvYÖÓñâÑõarñÐ{pïSãø= ‰ø’CƒW8·Ñ ½ƒ©Tˆä«·DôäÎZWM©y+#j@;ì§
¾ãºçÒý%¾WÏg(XâÆë@ÖOý@¨[TÜÌ‡â%îâI#Z5Ëvèý	¶«Ò”C^òMùhÒ« 0Îwûi£H·gú²ˆ|/GFt]S²|A#Ë(¶ŒFb.k¦Æøµ‹[}Dë¥A…g>Ã‹‡vÎ˜Zý¬ëjjúè¦ö ìåÈwä±æõåS]õó^à¶|[„[SÆ´Ÿ.YùW HO5Üd§)k@ê\7	Ï‚á1ëÐsGž·ïhéØ~*{rº,áT©ŠÖMµh»O»üJ:_ö.Çíôeœê?Ñ–U÷ÃªHëçÙ‘á&ˆïq5¼oztð?Ì(ðçkoÁ©´Å{H÷¦Ò†:fê)õÝ tP-%wG-–í§‹HŸãÍ&Ü8ïhG‡ß‡ˆÔ.ªÄï%š’º³óü"ia™€›ä9ª÷ãŸÅ’J]œŠžkóÓáâ5áO•‰¬¶,½–úÙßïÝAz­|–FêlµV§†RH×šÅ(õ®ñ9æR,ÍÔ6T.Ó¼GÕÐsÂ+L°Ü,ü:aìàHªÔÐ‡/Šù~ë3'uü˜#Ý]½XsØ½H@~RkßG¤ÎTBínay§ç¾®òV#ñ®®MÝ½G@kLuÝT.ra!;éáqõre¬Û¢w}ôdæ—Ço¼<Ö ¨·ÐÝ‚á­fŒñTî5Ìî:F·Åíæ&w°õ°Xon³Ûá™ZX–¸‰™µýëLl‹ÕÍÌë¦PulbVæñ•Êªº…½Ï6*SmdS[ ndR7Â©Eàj xhåx[šÝoiv)p—!²)ŽÛ*í^«´p¼VÖƒ‹âýl›ãg™âÂ{©Ý64ÃÏ1Âmip·eÜfÆ·AqÐ‡zÛVtÜµ`Ûww\Ümjj›WÛ:ïŽë¼ílfQz4øø^ì¹ÄæSí”­d¢†–ö¹ä-±Ú×yh+	Ým](¿b+“D“º)ËþëôRìDšÞéÊ…Ã¥ ~Qßïf»ºÿºÖéÛÝ×ÜÏüv@áù}¾èwßÕj
íx—Œ¦XGíjMµÓî+Ó—æwQõÛ à†*qm¯´]mMé(­í{ÛòçNùóHØs-îp°åÍò†u¼g.aJ$î:œ¹)çdµ¶®ä‹©¶t¹cº˜ê¡³%"ÁÕ‰ÚËëù}0¥ƒýÜ—¶f£ÔðFzÐ;#:¦·üvïvõ]f™ýBþÕ‡Úµñ# ¶®mŒ6MõÛ±nñ½O|eÂ
€³ ýŽ‡/Å.Ãa«Î’¼±z¦ÐÖ®¨–´Õv™/ž3j¡Ö,¾ç=ê¢uMØ•*’Å[ÉS©Ä»Uù~Ø©ÊÃXQGäH¼;Óþ?#ˆD®n€6ª]¾Xâÿ¯ƒÞ]Ã°§Wxe¿sÉÍÝ‘¨æt!Õ©µÛoÃéü®Á`1'GürüXÑÀHQŽ^öÁÃð÷‘¸±ÁƒúM>c .ÚºwÉüvÓÞØ‡Ÿß¾÷‚={—"6x¯eëFÿnôºP<å¯k     0707010005c84a000081b40000000000000000000000015ab2175400000e1e000000660000000000000000000000000000003b00000000root/usr/share/doc/opensvc/template.service.fs.zfs.conf.gz    ‹      í]íoÛ6ÿÞ¿‚@1¤ÅÙ¾¤·~ÉÖbÙÖÅ­ëÝv†!¡%ÊæEu$eÇÃýñ÷¼Pe'¶³Øi<8ÀÚÔ²‘Ïï÷¼‘¸çÏwùóì¹ØáŠËÜ@øE¥Ä™Û¸þ€¸ÝêîÙo™{~úû³¯h¾opÆÏžá ¯ÕbnlzÞ=Ûé?\îð•ãSÉq®Ú'ýlk|Ž°ê¿µ¶ªÂ2wt¥²f¦6¥.'çñ-©ÊdûnÐ?™’.èrª¬ö²LÂsr%3ñVL•L›A(óU–¯»J%:Ó	|g¢J¸7Á¯%¦œ)Égð£]Ÿÿ<Uâ¿µñ)>|+L&<|ÔŽX¥"•^:åGpßWx?h=3æ.­§jöY•Þ\XÑy‹Æ~•¾¤Ûqn’kTŠN”ÈtŽ÷Óßná¼*„.ä$\˜ça°¨ÿÈ£ïz#
S—~$¾×Y¦¬*}#9‘¥+0‰º¦ióHTi``¢vá!xï7øY)¸uQzyƒÃÇë!þƒõõù0¾Û°>È¨AZDµ‚¶
”ï °‘xŸ!*ƒæ
(*£áOí"p¬ª$â×}åVÒO…5ÆgŽ,žµž‚@ÿ|è<1Œ5>ñªµ#Æ¤µ/R/(o£v/Mõy5üÔøÏÚ­ÀH\£FÔÒzUºRV—O8J?¾2ßcôÕ® œ×:OÑ©KRNKäf¢™‹™Éë‚‚JÏ›”Òë™ÊÝ/þƒYâìÿF/ùAàš(æH¬ÆðÐ>aæâìô‹&5ˆÆFÙ XSbŠªöìýðnj<]!="qíExtb•Do7æA!Á8Sa!%CJïð7 Ì™Ú&Ê-«KM‚>¨‰/¼bæµ¤ZÏ½Ò\VV©¢ò—rlìáÙs{eW©¢,S©žrçÝhÅÿºç¬ä’ccàQå21Nœx¸ÿd >Vªüôëwb®ó\•Ã˜œF`•Itp¢œŒÀm¾	Ô*`d0K`œL‘„²½ˆè?Dp$>ÌcP´—v¢âÔGU®
¯ô8'R€qÔU·²-äÒype)ØŠr7/GÝd"A'êãŽ`h?Y&ÓI“ÉÌXNÖ[);ì…|f¶K§™½Ë’€ÄÀ4®&—IU»ƒcïZzÖ¦
è•ºN0{•Ëj‚õÌo8æß{½Ès3H<L›aÄàX0Qc]"Jà2É‹ñÓ 8TïH|
ÈÏb_Ö~Ew4p8ae	éü¹8œ^á§ÃWˆkÀ	àÄî³PÅÌý	ê5vÁ…Ð(ÂòCô9Â|op·à.ÝÄ!^ñ-Á²w5Q¶ÿ?•-UÞæ(3™CÄÒXáªt ¹¾N¦ð{-sà||vúêKÁúß^öÂ@3C›BÚâk
¡JWÛÕŽ¥‚Žž±‚®‚«×>N¾è©@*:yöò”¾`D€a$H aÙT‚¢%Gó#‘à@B ´aØf,z”ö½>ÛÄ>Zé9’o[ÿ‚€¤Àø*Öß‰á2ñI¤§ßÃ²¹ ‰5u%t&¾ˆs×¹ Ì–î‘c9<È`þ‚$L:`@+ãôHZY(¯ìHü ŽHÝHLpH¢uYßˆ„î€\x^OBÊS—¹.´ÇgSÎK3Àz¨–ÇÂ7â8Êºý`ú…N¬q
4u±
"qJ‚áÞ´IGâõéßÀw‚¬×\Gà¨CÇcÁï¼ê}#’æç†¿sž—,ß4Ò×GÔKcŠK°·&?’-ù·ô¼"ËåD¼8Å(x†µ¤ô‚8‡¥ÚñïÈµGÌCŒ½†: ¥µò3ø¾|¥œ»)_œ¾@&ï®Ë—ÞSÖO'¸æ‚=p$*„wrÉðä…òÆ‡âïº(TªaÚy•Ÿ4È"A<HöØp%ü-¸ÜÖÄPŸ•-Âx5&0®ðÈ\=æÒù+œEÐDˆV€­g£.ŸˆYL?ÛÀq2î#»ïËî•‘wôÝ6Q%¯|¡ËwZ“xHTûáÇlØ³?Ùê	X­úBÕèÔ’óÃô¡’)sƒÐæJ?Øáæ²ªt	ÂŽ¹/Ens€Ÿ :):­†€‰¾l%lÇ¸Ew¼_®Cmv4ê'aÔCÈömÙ$©{rÈ¾j(;Ç
H„³¶<¼ØþbÍb€UÎF—0Î¯µ¹œ+=™éµ‡ð-íó¾ÿSÌií]°vGâWó€ôs¥JÀ‡RÞ3 h*ØˆídézÀ7F‹rÞ»Jyy€kÙ§wÂ¹ƒ–‡ÛÐ¢J|‚e-K{LM£>pìíÊ,ä‘º ?1Vzˆ’IÜ.Ëä:¶e®þ
˜«Çl6ÁõB¯ùFÜ	š—“ÃÊP¢¾øí÷—ûÄÍá~¯æàu¸Œ46v¬½•ü»YqÇµÄƒÝ!#†Ð[Çow•ÂÖC7*¶Z¹ºÌÀ†a‚Ü6aÁ¼ã
f¬¦r¦A\¯Çm~þ‰3£¡\–%” ×ðåd#.‡¬)¢ÇÐîÞS»L/’&ŠÑG8Óòï’¨F£}Ó"s×ž+T êð<Än|~Ì&¼^ciW3ºûXS¼§H¿nè`ã?8Í>Í}ÄHÏæe£TÜn@
›r+€—ì*hø[g±î•àº¼vPh75už’§¨(÷€ÆŽ)áX5Á@¥ÜÏ\û†í¸PÑÿÅHð9ÐgE.ã¸€Õ!Ð­‡Lž€
pºií‘:ñîÎ{ï©Á	çs¹À-ÜùAWWàA¯®F\ÔAæ÷ý».~ùñgnÙšOMÞÛõÏAÍGñxWª‹©ñŠX”i,Ê›*øîmpªùgo¡Ê*_[˜m`M	ã<OŠôüÊÕiÐÜ,)&Vø~Åf´·Í¨®x+\˜ç×óó«0ÃQsfÐoEkbc7-ŠZš§DÁ×‘»tätáU³«N+üÁ*Ì¼ÈÛ,Œm°6n8’‡·¦KFAkÒ]3meöž¦Cþ—€‰˜PmÊ›¤Üâë½¡+xøÖ¹l¹½Ú«ÜºÚ`Ã1®Û.“:§~U\n p'Ôèf¸zû‹SY“¢;JçúxWUÔÈ“6ë28âbC2 ã°Ý•uŠéxqpD}š\¼`Î>5Žß\ˆ/)4°Â©mÍV¤RA$_n¼u@D&wÖ¸jLÍQÚE?UàŽëžKç[¸W³F(XâÆë@Ò-ý€P¿¨¨™Š—¸?ˆp×Xv¬ÎÈ®JSiË7E÷yšdÆs‡1/Er¨=“Ë"ô½h6Nà}u	åY†±e4si‰çâ{¨]üòùÑA…9Ÿ‡áÅC[1¦F?›ºš†œÁ4Âž”½|âŽ<òÑ´¿|­«~Þ¸µ“$ÇV—1ÍO›¬ü;P¤§j²Ó˜5@êlë„VÁ`š6ôÜ¡çí;ÚBz2_Êžœ6K¸VªÂ½AS-šîÓ6¿’žËÞnÜì ïâTFGV=«"­¯²'"ÃC?c=Ø7üO3
üéÞ8•¦xéÞTb¥fabfÔSì»Ð]´˜Ü}j°l~Úˆ4ào6¡ÆyãøtñSˆHÍ¦Jü‚›)±;ë"Ïo“¶	¨Iž¢z?þ±@PœA©ØA©èJ;/#„›7„?U&²:²t,åÕî÷n!½Vœ¥aÇ…ºY®Õ±áD§ØÒ¶fJ½{8ÇìÄâJmí€Êeš÷¨zNh‚–›ïÆ¥Júà¢˜ž·9sR—‡éöõbÍEû"fø‰ÕÜG¤nT‚ína{§ç¾Öy«‘xg­±í{¸Çdm]ùÈ……ì¤‡Çú%äÊ8DïþèÉŒ·ÇwÞÐ¨ÐíÀð–3Æx)÷fw£;âöp“Ûl=,6›Ûav;<!SÛ1³¦¿a“‰±z˜y=ªƒmÌÊ^ùñ¤¬ªÝØûÓFeª­lêÔƒLêA85¬Š€­\K³Ç-Íîî.D¶ÅñX¥=j•¶Ž÷ªÓzðQ|œ‚m;cüS¦x„ðQj·-ÍðÏá±4Øo·ñmQô¡>Â¶×ŠnKƒ»lÇânÏÅÝ¶¦¶}Õp¬óö\çmmg›0‹2ÐË ÁÃ{±g?ˆÍ§Ú+WÉDžsIGb5¯óàQš{ ðdFÅ;¶2I`bë²ì¿N/ÅI¤é“¶\¸èÑ‹ú7¾a¹¼ÿµÕéÛ_S?óÛ„Çç|á¿¹«ÕÚÓ)'u5pÛÕêê¤9W¦/OQåc à*ñM¯´[nMi)$ÚØ÷väÏ^ùs ì¹w(8òf¯¼!oÅ™;˜‰»gneÊŠ¬¦Ã6¢ÁZ¾˜êH—=ÓÅTO-	Ö&l/¿´óÇ`Jû_¸/mÃA©áô wBtŒoðqïnù]f™ù !~õÁú&~Ô6µá¡©|ëßÇÄW&¤ pxÞñðµxA0FØª›$¯ž©hk®ÈJ<j»Ì/	µàAk>§3ê¢uM8•*’EGÉc©D§Uq?ìTåá¬¨#r$ÞÝhþŸD"—@Õv/–ðÿë ÷F×0œé^Y§Ä\Rsw$*‡©y]È@ulíæc8=Ÿ,¦äˆ^Ž+*ÊãË>0ú<760Q>ä3â¶£{;æ7‡öÆ>|õøÞ[ÎìíDlñ^ËÑþ%Üèÿ#D
h    0707010005c84b000081b40000000000000000000000015ab2175400000f26000000660000000000000000000000000000003e00000000root/usr/share/doc/opensvc/template.service.ip.amazon.conf.gz ‹      í\moÜ6þž_A (6Á­UÛ—ôp®cÔ×¤@pmSÔiû!Ö\‰Ú%V"UQÚõ÷ãofHJÔ:ûâø¥ÞBÛ4±õ2"çyæíÓ§wùyò”ÝáÅÉbÈªe!Ïù'­n+î? înu÷äƒ,ž~|ò-Í÷•›ñ“'8î™X.t™œ´²€ãwøAõ˜X|œ‰æAïËZà‰RüYËR´#øg†Î¥žK#µ’jrÒ9“ˆ”×YÕúg­è„TSQÊŠ«Ø=(<egl*xâG!ÌW”ö¼)D,SÃ5¡àÞ˜´O01\ñ~*XQ3¸@dÜTð¯,X¥7FÇ’W9•…â¹8‹èzÉ³€§<ËtŸ±9Ïj¸Gd™a
÷s¥+'Šq¦ÄÂ=8Ã“¤ÆD0Äo%@òåqtü¯èß‡Ñ7ÿ\gÌMÌ1rc€G=Rtý-î…¨HÏ±åÆc¤™„J<Z×uÃ,Ò&/á@%JþRUüŠI.¦²¦à1x>"“¦b:ÙQÊä,²‚?|wJ³8ûH _Ç0[>r—‡?§4ÌkÏD9J¸Èµ=Œ…7P?: U¨D&€ˆ9i	ËþçG¶3K8=88€c§hžgÆËIUÁPÑÄ¤_¿ùáü·ßG]0àzÐq_EP'Å¬W‰˜ÿ¥VíO<—-h2EÃ#+Ñs0c°ÆxÊÞB]üþ=[È,cU¹D37gä(ç2!“EÄ¾çŠKdšŠMK3>C“C	x/9‹!‚nŸ¶+VðâH@ê&#Â€°S­×cíÐ_öcÐ¯¾p8—ìí/>J‚“%\÷½Ê ¡@Ýc#ìýÂ üÉ5¸üÚˆ´ÎˆntíüF;äb*ÔAËÊà(^ÿúˆKÄ//áÚz¬œâðs&g"#BwfXM¹
dUSSpº “:¦¸¾ìEQ ûCVS:ïRã¤”sTC—Í.ÏùÂì&¡ 3©0½qñm£)$ÊŒêøÞ™CsæaÂ\ @Œ`¬5<J­†º
ÆÐÉ "›”“‰(1Ò¡©Y•Ût§1ŒÅ¥°‘Ñ°¦ºÌy…‡!HžšylsäS^ðwÎŸÀ9à—8²Zeh³©•9ZÉ³`D]_Ù¢,±óÜÀ”62ušÊ«½#Ë}ùÎó$!+2«!ïým‡0êïswª¦µYƒûÁé
«L$'ƒ{6ûð„pdÖãÄF§
a‰&T”®¨”Ú<Æ)‡ÖÎÁ›ˆgFc,KØxI7ÙTïµQ¹“ˆLTVf‹‰ÿ”º†s2Å«Fpzä€}C¦ÌÝøî^ ýè0¢ÿÖ!ÿÉj°‡ÝÃþ‰Œ +)ê9“j V:boSÔúÐ_ÕñßEVçc¸ŽX‚L2=†xoËû+•¸ª.:ðŠŽ­][º*´Ù?¨>|¼ËX½ŠqI ë˜ƒ¥ƒƒkKì´Ðeuöõ)L Ò±ÎÎ>œœN1Ç¤ãQ^)ÇÓÚiå“KtÕ¼bàz)€rvñëï‰®Á¦€áÊ‘ü0Q¬¤·{˜V*'5Æ²‹OÒÚ®XöÀÐæzžsÓ{pO0O¨/È0Á	CÄM–Yjk†¥ÖUPï>ÛT:õ¢´D@âß7*ÅóˆýD„ÐPKW=5’;¾n—v1Ä¥
ŽWìYRçù2¸íZeBî£#GúÜ&6wÀ
I‹o.fAe§ã
œOJ5ÞüÅØzÈ8~ù2²ÿG‡xùññt—toû†ncß¼ðñ…Ôñ*¸ûåú SLFqQ›¿9oà·Q•˜V°MGÅ1ý€cþØuc6,fÀˆcLñ‘Q™Í š´ž¡z¡F‡¿‘6«¥&]Âsƒ%+¹švÂ‡GÃc<pxpŒ°:œ V<°Ì\ä=˜÷&¨ý­J‡«"?ÇÉ;Ü\Äm+¸À€‘™‚øâÍ&Aç=Á €ÿ¿¢T"óƒu%#¸i,†.âÔ¦†Ja‰‡_0«H2ã¡»/êƒ±®5lÔ®ÊÔ®ñK4Š|ÎZÊb²
c=“"ŠI®_èÃ`ÂcŸ±B”¸°%3áU‚±³!‡ÿ`‘³°ÍîûøT)jŠ£n¼mcßŸ5ÔY=ùvõ/´Ój‘ãR ¨¶Ä(i9Ö¡>ãð.hõcéÊWkV‹€\™M…aµÙlñaÍé
šŽÀ°jwø~ G$®x^dbˆ$úQªúŠÅt‡²ƒ#ðz\\Í’9dð‰ÍABöÛ{~m,öF*™1¿+qú¹ŒKH™Á@ª '8èMê$b/¿ú®q²à7‚P‡ÆŽ¯9î\./´½6ä<)o¥oŽ¨#­óV…¥Îzòo$ÿŽž÷œ¥Ÿ°g”Õ=·…›P¨ƒ‡iìÏÈw5Uˆ.ÆÎd–ur0æŽ¯Tü[9	H5$Ã®0Äõ»¼ å¤©sëQ.¼“K†'/)”{Š?Ë<‡"¦¹4KFïÞý²ƒ´Î¸ß©pƒƒÛ|Üõ	PÙÒÍB x*üÈL=6KÓø––¤¬– z@´l;ŠÚ|"d-.7má8wÏî›²ÛÈOÝ­õ7ôÍ.QÿZ^	…n€ûxY	ãjH°ƒwéAÇ`ü•ßÇÀ}
eËã®óÃô¡À=*5	¹AhÛHŸ-ì0Zù a=EnJ‘Ï9@lÀà¬Õª˜èË®…í·àŽWìÅ&Ôæ½Q?
£þBvß–ÝöèÑ“]öUCÙ9@"œui‡Ú¿'Ö<ôXålu	ãl&õh!ädÚÓëNÂ ž±·ï`Š§\Új7b¿#˜' dµB>”ò@‘«`;k˜n²tÞá¢E9ï:dRØ•±wˆÞÙ:õŽhQ%>Á².ì¹rêÇÞìLA)sðc‘¢‡H!™Ä‹1ÃíCWýå0×
³Yê^!ô¼ÈWl-hŸì_P†õÙ‡Ïï7xÄJÍá÷šPc;/Ç²*9B¿û¦'Ú<¬;´H¸!T:€¬íž]`coGÒ"v¡q›ÒË•*†	ÚÝeÚ[
Ã˜ò¹qbœ”¶Ï0>×Êe®–Ø7‰~¾|‚L²W„åßÙôN ï©Í Ó‹ØG1:„3U_s¢öUƒÌ¶a"öÏCÜÏÙdÛ¾h·Ê;®f´÷YM‘jí›=œñïfg××9¤gå•
&Ý€ä6åVØµ‡‘¦–±L„ßl¡ðŸj*ÕÌ@¡ÍÌT×YBž¢ @Üa ;¦„cáƒkìò®}[W—­èÿf$ø+Ð·Š\Å·j'ŠnÝeò”ƒÓLë
©îî¼­Œ÷—´g½àK‚±# ]^‚½¼ŒlQ‡ý¬¶#Þö,¦:ëìú¹gâvµcŽ÷Zu1Õ0^æv”™JÎÿª Œð¿vªJQÕ%Ì¶Ž±¦„qžÄyrriê$hfç“’ím~T—v+Ì`SîÉlqrÙôü»ó0ƒp×¡í´(jI;%
^¸ŽÜ¤}| §ˆ]5»lµb\‡Ù.òú…±-Ö¦Tß¿5]2
Z+À­~¢íRÅnOàq:ä_4LÄ„jS»I:À®¸®÷R6 9þ‚u®Zn§öÅ*·.¶Øðë&‡K¹Ì¨†¿..ÓìÚÅ[± Fç0ÇÕÛßl{;*º¥4ö}°¤Î–ÊLØÕØ•aŸÁ=ÉÜë.[`[—uŠÉx¹wD}œ\<·ÜúÔ0~q!^Qh°
‡q´u°Ì¾¼Ôm‚›ˆ–Ü©wÕÔæd´¾	}'UÀ—,(™^ÁGÀ]‰‹í+ }OÒ-ý€ÐjYÈ(ÅËA p×X6À¾(²+¥Õmù&è>g I«‚\ƒ‡†KÜ²Q '×µrmQè{Ñ8RlœÀûjeKYŠ±%ŠØ‚—Döj—juŠöÑ­A¹9Ÿ¸á…C»fL^?[{ÃÐ½3/ìQÙË…ía&MûË3|_8Ì{·f’äØjbà?M²ò‡£HG5´™Ä¬Rç_á™ƒäBÐÎ¹²ž·ëhs^‘	ü¦:rš,a&D{ƒºXú·ƒšüŠW¶ìmÇ½ù}ÄîŒzV=«­_gO@†Û0 |ÆfXß´wð?Î(ìð§{ïÀ©øâÝ¥{SÞvqÝ<Á¾›1¾¡XLî.<–þÓD¤¡ÍñæHeVá8.ÎvÉoª´/•`)‰ÝYçYö9in› Yi£z7þYß@©ØA©èµv0»ŒànÞþ„Šùþ}ÝÅ^°Ô®Øæòöõ»h³4÷öÈJ­Ž'2Á¦5‹PêÜãßEkßj¡wÏ ZN²U]Ï	í@Á2½´û„¡ƒC©\‚>Ü—kàó¶gNb´Ï‘îÞ^+mæ9õÞC†—Òö‰+c»›ÛÞé¸¯MÞ*boÊR—€M©éÕ‚².ªÀ…¹ì¤ƒÇæ%äßgéÑ»1z<µÛãwÞ
[Ð¨{èîÀðV3Æp)÷fw£ëq»½ÉÝl,¶›Û~v;<"SsÛ·13ßß°ÍÄz¬ng^·…ªÁ`³ÒûW~<*«j6ö¾Ø¨t±“Mõ@ÝÊ¤n…“G`3Pcl­ŒúÒìaK³µÀ­CdWû*íA«´p¼QÖ¿Gña
¶ÝŒñ‹L±‡ðAj·ÍðKŒ°/î·ŒÛÍøv(ºP÷°ÝkE·£ÁÝ¶¾¸»çânWSÛ½jèë¼{®óv¶³m˜èÈipÿ^ì¹Ä6~u~•„´= €c.ìŽ-c˜”µRÝ×é9š4åÂy+ˆ^Ô¿*ðËÕÍxúbûg§ÔÏ|6Œ¢è¹ýÂAüÝvµê\Vô-ƒºš
ÛÕêbà¿W¦+ÞÓq_ qå{¥ÍjkJC	 ÑÖ¾·ž?÷ÊŸ=aÏ¸Cy@Ï›{åéx'Î¬aJ î&œù,S®Éò¶6òE=]î™.ºxìl	H@dù?ÅÍì"k    0707010005c84c000081b40000000000000000000000015ab2175400000e99000000660000000000000000000000000000003b00000000root/usr/share/doc/opensvc/template.service.ip.cni.conf.gz    ‹      í\moÛ¶þÞ_A Üâ:Z´û¥Ár÷ÛÚaé¶EáÐe‘DM¤ì¸¸?þžsHJ”S¿¤±»x·‰^ŽÈó<ç<òÓ§»ü<yÊvøAq²2³(‹¹q;ü€¸ÝêîÉ{Y>=þðä[šï+œñ“'8è±˜«*9kŸ]GnàÜÑ?¨«’3Ñ<ì]U<Q‰¿kY‰äÎ‰²R3©¥*d1¡“?ñLÓ™D¤¼ÎL;î7ª ²˜ŠJ^Äî9™à)»`SÁ?Á`Ê¢²çu)b™Ê®™ˆîI1ðÃï¦‚<‡[Rfà÷ïß¼ö*bF±2«'ðL£"¸ë[¬
Ã%ˆ{z¼JÑÍ%£J&ÿ¨º­>}WB«ºŠ“‰W{£¬ŽÒ—!:z=4(ó·@"ÓaY¶fðÈ2³^ó­æA™¢Jyì½Ï>ŸÊxÊÞ–¢¸úó{6—YÆLµ@´áñá E5“±â‘,#ö=/ØX°D¦©¨DaXZ©œÁðY¡Áœ%)øQáÑîI1×OÂßá8¤œ&¡n2"!ì	HÀ<Uj5Öxïñ´xsåp®ØëßO°<íMlËâ
(„¶uŒ°wS©üŸ+mX­EZgD7ºvÁx0ä|*Š$‚VFã(~øý7—ˆ!^^Áµõ¼·f~ÏäÈˆÐš)/YfªtÀ¤Ž ÈV Ðn"ö—4S:ÏsþÏUr†jè²™æÁ3vÎ³LÅÜˆ6ãYºY¦½|Këð¸ÖrRÀÄ1• n÷À9K D×šBRèQ]ÂÀÏš3;±†4L$jBŸµ£eÿkŸQ F0V
UtŒéJcè€µ%¯ ƒÈUr2°ŒLÍª<r!.†±±¸p(²°¦ªÊ¹ÁÃ\³s=‹Ñ‹èœ—%üÌyÁ'pø%.†¬.2´Y„ÔÊ97ç?0¢®¯lÑ–Øy®á	Jé:MåíÁ‘e_¾ó2IÈJ­ÕrÄ×jÈ{?DÛ!Œúûä]„Ì´Ö+p?:_‚a™	$âDrpÏzðŠ¯Ç„/U_V'ÂE¨ÊptùC›ÇØ@â"eà°<¼ƒ×»Ì´ÂX–°ñ‚nJT|>îµQ¹“ˆn1ñŸJÕpN¦xÕNÜ°oaˆy Û¥‚ðä8¢ÿV!ÿÑj°‡ÝÃþ‘Œ K)ê9“Å õ{¢Ö‡Múo*Oà:b	^0ÉÔâ½´i
=‹qk.:ðŠŽŸ¬JÜ–JTï?ì2V/‡âLjÓuÌ˜m‚¹•‘i‘¯ÃÌí¼T•¹øú&`T¬²‹÷gçSÌ1éø‡ B”WÉ1Ætvj|r‰®š®—(gW¿ÿiÁ‘èl
"ù#’&ÂÅ?g÷0­TNj d—%<ž¤lak±†€6W{ðœëÞƒ{"€yB}A†	N"n²hÌRY3¬”2A½ûl]ýéÔ‹Ò‰kÜX¨”Ï#ö+BA],]õÔHîø¸]ÆDJ—*8^±gIç‹à¶;•	¹Ž0és›\ØÜ($–^>fAe§bÎ'¥oöbl=dœ¾|Ù§Ñ1^~z:Ý%ÝÛ¾¡ÛØ7/||!u¼
î~¹:È”“Q\ÖúßEÎ{ø­D”¢Ht+Ø¦€£—Ù{ó‡®ƒ´aÞ0FcŠ,L‘Ù¢Iëªjtø‰´Y.5é®¬XÅ‹‰`gìxx2<ÅÇG§«Ã	`Å«ÁÌEÞƒ¹?0A½èGp©D‡«"¿ÇÉ;Ü\Äm#¸À€‘ž‚øâõ"Aç=Á €ÿgQ"óƒu%#¸i,†.âÔº†Ja‡OŽO_0«H2ÆCw_ÖGcUk Ù¨])"
]»2Äs,Q@(Vð8#h)‹IÆz&E “\¿Ð‡Á„Ç >c¥¨paKfÂ«cgCÿÁ"gn­ú5©RÔ:G)`ßË“Mìû»†:«'ß¶þsHñ)T[bT´ë‹PŸqx4ú±Ätå««E@®Ì¦BŒµÊ°Út•ŠjNWÐt†u_‹ØOàˆÄ-ÏËL‘D¿È¢¾e1Ý¡‡ìè¼/4®fÉ2øÄæ !ûmŠ=»3{#•Ì˜ßU8ý\Æ¤Ì` ‰Uˆô&U±—Ç_}×8Yð
Á¨CmÇ‚×œv®‡çÊ^ržˆŒ”·Ò×GÔ‘Rù«ÂJe=ù×’KÏ{ÉÒŒOØ3ÊjOžÛÂM¨‡©íïÈ·5Uˆ.ÆÞÈ,ë,ä`Ì_©ø·rþj:H†]aˆëwyIË10H]çÖ¢\x'—O^P(÷>—yEL;si–ŒÞ¾ý5di=6œqS	à·ù¸ê ²…›„@5$ðTø‘éz¬¦ñ--IY-Aô€hØ
vµùDÈZ\nÚÀq2îžÝ÷e·–E‡Ú?RÐ×ÛDý;y%ºîã…ÚÕ`GoÓ£ŽÀøßÇÀ}ŠÂ–Ç]ç‡éC‰{TÅ$ä¡mCþ1}6°CÏiå„õ¹/E>å ¯ :ÎZ­º€‰¾ìNØqîxÅ^¬CmÖõ£0êÿ dû¶l’Ô>Ùe_5”c$ÂYWvx¡ý{bÍB€UÎF—0În¤Í…œL{zíÄ!ü²àöú-L1ã”K[íFìOó€4s!
À‡RÞ (rlgÓM–Î;|C´(ç]…,P
»2Ñã­So‰Uâ,ëÂž+§>pìÍÎä‘2?1)zˆ’IÜ¸ó8Ü>tÕ_s5˜ÍR÷
¡çE¾b+A3|rxAJÔgï?<ß'nðˆ¥šÃï5¡Æ"vY¥©8Bû¦'Ú<¬;´H¸!@æÖÀ 3ÅXîGÒ"v¥p›ÒË•E
6´»Ë´·†1å3	â:Å8Ê>ÀøLI(—y±À¾)Hôcðåd’½",‡üÎ¦op1xO­˜^Ä>ŠÑ!œiñ5'ªÑh_5È¬`V âð<Än|~È&ÛöeÂ–X\Íhï³š"ÕÚ_×7z8ã?8Í>Î®¯KHÏæ…W*˜t’sØ”[a×6FêZÆ2~°…ÂÌT7
m¦§ªÎò%âÐØ1%\c—wí›ººlEÿ/#Á?¾Uä2î¸U;)ˆèÖ]&O@98õ´6HpwçµÑÞ_Òžõœ/4ÆŽ t}ôú:²Eö³þøÓå¿¼³ýó©Ê:»~î™¸]íÆ…ã½S]LŒ—¹Ä¢;Ê´Q¥óÿš* -üŸ…ªJ˜º‚ÙÖ1Ö”0Î³8OÎ®u„MÏâ|R±£ ½ÍêÚn…ilÊ=»™Ÿ]»Fþ<Ì Üuhcc;-ŠZÒN‰‚®#·iÈibWÍ®[­Øwa¶‹¼~alƒµ©•ÅoM—Œ‚Ö
p«Ÿh»(b·'ð8òo
&bBµ©Ý$`WÜ ×{)
 Á:—-·Sûb•[—lxÈÆu“Ã¥\fTÃß—©	víâ­XP£s˜áêí¶½ÝRû>XRç%Ke&ìjìR‡°Ïàˆ‹žd@Æ-`[•uŠÉxqpD}œ\¼´ÜúÔ0~q!¾ Ð`ãÖ4hë`YÂDònÛÜD´äN½«¦î4'£õMàè;©¾dAÉôÎ8îH,Xl_íxrÆhé„šE)c /G4Z4Â]C`Ù û¢È®
UÑ–o‚îó4iU+ðÐp‰[6
ääª.\[ú^4Ž'ð¾º È²Ž,ÅØElÎ+¢Æûj³<EûèÖ ÜœÏÜðÂ¡Ý1&¯ŸŽ½aèÁŒö¨ìåÊö0“¦ýåYvó^À­™$9¶º1ðŸ&YùËQ¤£HZÊLbÖ ©s…¯ðÌ@r)hç¼°ž·ëhsnÈþ(:rš,áFˆ÷U¹ðo5ù7¶ìmÇmô*NugÔ³êË°*Ðú]ödxÂg¬ç€õMÿãŒÂºwNÅï.Ý›ò¶»xŒëæ	öÝŒñÅR`rwå±ôŸ&"mŽ7›@ú(3ƒã¸º|ã"’ßTi_*ÁR»³.³ìSÒÜ6²ÒFõnü³9¾R±+‚RÑ;í`vÁÝ¼!ü‰"æeÏÒ}°Ô®Øæòöõ»h³4÷öÈR­Ž'2Á¦5‹PêÜ-½aïÞ=ƒj9É:Tu='´AËÔÂî†®yÉÞÅô¼Í™“r¤ÛÛk… Í<§Þ{ÈðãJÚ>"q+blwsÛ;÷µÎ[EìÇªR`gSjzµ ªK¸0—tðX¿„\âû,=z÷F§v{|çà-á±=€º‡n†·œ1†K¹÷0»û]ÛÃMn°u°Øln‡ÙíðˆLÍmK<ÄÌ|Ã&ë±z˜y=ªƒmÌJ^ùñ¨¬ªÙØûl£RåV6Õõ “zNõ@±1´2êK³/[š­n"ÛâØWi_´JÛÇ{Õiø{¿LÁ¶1~–)ö~‘ÚmK3ü#ìKƒý–qÛßÅAê¶½Vt[Ü½`ë‹»=wÛšÚöUC_çí¹ÎÛÚÎ6ad #§ÁÃ{±g?ˆ­ýê8ü*	i{  Ç\Ø[Ç0©ê¢è¾NÏÙ Ðô ).[Aô¢þm‰oX.oÆŸW2¹xvNýÌÃ(ŠžÛ/Ä¿mW«Ê¥¡oÉÔåPlW«Ëÿ^™®4zOÇ} <PÄÆ÷JëåÖ”†@¢}o=öÊŸaÏ½¸Cy@Ï›½ò†t¼gV0%wÎ|’)wdùÛ€kù¢Êž.{¦‹*;[Yþ-Î&–ãi     0707010005c84e000081b40000000000000000000000015ab2175400000e7c000000660000000000000000000000000000004000000000root/usr/share/doc/opensvc/template.service.ip.crossbow.conf.gz   ‹      í\koÛ6þÞ_A Üâu´$h÷!KƒåÝ(vé°tÛ‡¢ph‰²	K¢&Rv]¼?þ=ç”(§¾d±»x·‰.Gäyžs#üôé>?Ož²=~Pœ,‡Ì,KÁâJi=V‹‡‰ÛãÄíWwOÞÉòééû'_Ó|_53~òG>Ë…ª’‹v ²LÄ\|0pòdÔ’ŽUÉÇ™hžö¶ªž¨Ä_µ¬D;Œx¦éLY©¹ÔR²˜\tÎ$"åufÚ‘Ï_àaYLE%/b÷˜Lð”]±©à‰ƒ`0eQÙóº±Le×LD÷Æ¤¯c¸âíT€L#ª”Ç‚<”#
KUÕ2H–<ÉY¬ŠTNêŠ¸ Q_{…‚îaˆ›´þªÜŸ¸‡ÆQ…øL:WsQ±ÅTÆSö¦ÅÍß²…Ì2fª%3ŠiÃã3pŸÕ\Æ"0JYFì[^°±`‰LSQ‰Â°´R9ƒá³B%%à½
~TCx´{RÌµÀ°Á7xi §I¨›ŒHZ´êT©õXãýGg_‡Dû»_nÎ{ý+ãIR	­™J	YÂ´ª«X¬2@h'P·ÀØ{;•šÁÿ¹Ò†ÕZ¤uFt£k—ŒÃ€A.¦¢XCB he4Žâ»ß~q‰âå\[§8üžÉ™ÈˆÐš)/Yfª`
N7 `RÇèlXÂÁ
ÚMÄþ”fJçyÎ?â¹JÎQ]6Ó<xÆ.y–©˜qÅæ<«Aw"Ë´—o	öãâZËI/Ä"T‚x¸Ýç,Ýh
I¡Gu	?>shÎìÅÒ0‘¨	}ÑŽ–ý¯}D€0ÁX)xTÑ1¦aŒ Ö–¼"WÉÉDTÀ225«òˆL¯1ŒÂ˜€C	=€â[Îæš]êyŒˆ^E—¼,ágÎ>sÀ/q5du‘¡Í"¤VæÈ¹9ÿu}e‹>°ÄÎsOPÚH×i*?Yå;¯“„¬ÔZ-G|­†¼÷C´Â¨¿OAÞEÈLk½÷“ËV™H"N4 ÷löà	áÈ¬Ç7ˆN'ÂE¨ÊP~9´yŒ$.R;ÀÃ;x±ëL+Œe	/é¦DÅ3ðp¯ÈDdà8èt‹‰ÿTª†s2Å«Fpzä€}CÌÝøî^ ýì4¢ÿÖ!ÿÑj°‡ÝÃþ‘Œ +)ê9“Å 4*b¯SÔúÐ_ÕñßeVçc¸ŽX‚L25†x/mšB€°b :!¸èÀ+:~¶(ñ¡Túø z÷~Ÿ±z5gR›®cÆlÌ­„ŒìDŒÄh¸xfn—¥ªÌÕ——0£b•]½»¸œbŽIÇß¢¼JŽ1¦k´Sã“KtÕÜ0p½@9»ùíŽ¤Ò“RÀ@É‘ü0Q\Ho÷¾vÙxÈ.Kx0<I)â‡ÅÚ\ïÁs®{î‰ æ	õ&8aˆ¸É²1KeÍ°RÊõî³Mõ§S/JK$þ­qc¡R>ØÏDu±tÕS#¹ãàv)]ªàxÅž%už/ƒÛîT&ä>:Âp¤Ïmras Xzù˜•Š8Ÿ”j¼ù‹!°õ"qþòedÿG§xùùùt—toûŠnc_½ðñ…Ôñ*¸ûåú SNFqYë9ïá·QŠ"Ñ­`›ŽÊ	búÇü¾ëÆ mX4Ì€Ç˜â#$Sd6ƒhÒz†ê…~"mVKMº„kç+Vñb"Ø;žÏñÀéÉ9ÂêpXñÀz0s‘÷`LP/ú\*ÑáªÈÏÁqò÷qÛ
.0`¤§ ¾‡x3ÄŸÈGÐyO° àÿQT…Èü`]ÉnK¡‹8µ®¡RXâá³ÓóÌê’Ì‚ñÐÝ—õÉXÕÀH6jWŠˆB×®ñKŠ|ÎZÊbÒ„±ƒžIHÅ$×/ôa0á1ˆÏX)*\Ø’™ð*ÁØÙÃ°ÈYØ€F«~MªµNÅQ
Ø÷òlûþª¡ÎêÉ·«AÀRŒö?T[bT´ë‹PŸqx4ú±Ätå‹5«E@®Ì¦BŒµÊ°Út•ŠjNWÐt†u_‹ØàˆÄž—™"‰~’EýÅt‡²“3ðz¼Ð¸š%sÈà›ƒ„ì·)öüÎXìT2c~Wáôs‰ûC4Ñ¡
q‚ƒáÞ¤J"öòô‹oà'þB!x u¨íXðšóÎáâðBÙkCÎ‘‘òVúæˆ:R*aUX©¬'ÿFòïèy¯Yšñ	{FYíÙs[¸‰µ ñP"µý¹ó¦¦
ÑÅØ™Ì²ÎB¦ÁÜñ•Š+'éÏ¡¦ƒdØ†¸~——´ƒÔun=p Ê…wrÉðä%…rïCñw™çP$À´37fÉèÍ›ŸAvÖcÃ÷7• npp›; >*[º9@h TCO…™®Çz©a_Ó’”ÕDˆV€­`gQ›O„¬Åå¦-'ãîÙ}_vkùQt¨ý=}½KÔ¿“WB¡à>^¡]	6pò&=éØ Œßø}Ü§(lyÜu~˜>”¸GULBnÚ6äŸÒg;ô‚V>@XO‘ûRäSð ã¬Õª˜èËî„í·àŽWìÅ&Ôæ½Q?
£þBvhË&Ií“]öUCÙ9@"œue‡Ú¿'Ö<ôXålu	ãl&Õh!ädÚÓk/á¿ÏØë70ÅŒS.mµ±?Ì Ò,„( JyÏ  ÈU°5L7Y:ïðÑ¢œw²@)ìÊ8:DO÷¶N½#ZT‰O°¬{®œúÀ±7;SGÊüÄX¤è!RH&qãbÌãpûÐU9ÌÕ`67­r^ä+¶4Ã'Ç”¡D}öîýóCâX©9ü^j,b×ÕXšŠ#tð·oz¢m`ÀÃºC‹„‚Qda0SŒå~T -b7
·)½\Y¤`Ã0A»»L{Ka8S>— ®SŒ³A¡ìóŒÏ•„r™Kì›‚D?_>A&Ù+ÂrÈïlú'ƒ÷Ôz€éEì£Â™_r¢öUƒÌ¶a"ŽÏCìÇç‡l²m_´[åW3Úû¬¦Hµö×ÍÎøN³³ëëÒ³Eá•
&Ý€ä6åVØµ‡‘º–±L„ßl¡ð3•ÅLC¡ÍôTÕYBž¢¤@Üa ;¦„cáƒkìò®}[W—­èÿe$ø'Ð·Š\Å·j'ÝºËä	(§žÖ©îî¼6ÚûKÚ³^ð¥ÁØ ‚noÁƒÞÞF¶¨Ã~Öï¸þý§·¶Ÿ`1UYg×Ï=·«Ý£p¼wª‹©‚ñ2—XaG™6ªtþ_S …ÿ³³PU	SW0Û:ÆšÆyçÉÅ­®“0 éyœO*v´·ùQÝÚ­0M¹³ÅÅ­›aäÏÃÂ]‡66¶Ó¢¨%í”(xá:r;öñœv vÕì¶ÕŠ=pf»ÈëÆ¶X›*QYüøÖtÉ(h­ ·ú‰¶Ë"v{Ó!ÿª `"&T›ÚMÒvÅp½w¢°ðÈñ¬sÕr;µ/V¹u¹Å†‡l\79\ÊeF5ü]q™š`×.ÞŠ5:‡9®ÞþnÛÛQÑ-¥±ïƒ%u^²TfÂ®Æ®tûŽ¸èIdÜ¶uY ˜Œ—GGÔÇÉÅkëÁ­Oã÷â
Vá0nMƒ¶–%\@$ï¶ÁÍ@DKîÔ»jêNs2ZßŽ¾“*àK”L¯àŒ#à.Ä‚ÅöÐ¾€'iŒ–~@¨Y–2ŠAñrH£E#Ü5–°/ŠìªPÅ	mù&è>g I«‚\‡†KÜ²Q 'WuáÚ¢Ð÷¢q¤Ø8÷Õ@–-qd)Æ–(b^5.ØwP»˜Õ)ÚG·åæ|á†íŽ1yýlqìCÎ`¼°Ge/7¶‡™|4í/ÏdÙÍ{·f’äØê"ÄÀšdåOG‘Žj i)3‰Y¤Î¾Â3É¥°oZÏÛu´97d¿9M–0¢Ä½AU.ýÛAM~Å-{Ûq[½ŽSÝõ¬ú<¬
´~—=Â€ð›9`}ÓÑÁÿ8£°ÃŸîÝƒSñÅ»K÷¦¼í.ãºy‚}7c|C±˜ÜÝx,ý§‰HC›ãÍ'>ÊÌà8n®qÉoª´/•`)‰ÝY×Yö)in› Yi£z7þYß@©ØA©èv0»ŒànÞþDó²gé!XjWlsyûú‰]´Yš{{d¥VÇ†™`HÓšE(uîñï¢µoµÐ»gP-'Y‡ª®ç„v ˆ`™ZÚ}ÂÐÁ¡T.A¶(¦çmÏœÄè˜#ÝÁ^+mæ9õÞC†WÒö‰"Æv7·½Óq_›¼UÄ¾¯*Uv6¥¦Wªº4sÙIÍKÈ%¾ÏÒ£woôxj·Ç÷Þ
[Ð¨{èö`x«c¸”{³»Ñõ¸=Üäö[‹íævœÝÈÔÜ¶ÄCÌÌ÷7l3±«‡™×C¡j0ØÅ¬Ôñ•Êªš½¿mTªÜÉ¦z dRÂÉ#°¨16‚VF}iöyK³µÀ­CdWû*í³Vi;àx¯:­âç)Øv3Æ¿eŠ=„Ÿ¥vÛÑÿŽö¥ÁaË¸ÝŒo‡â uÛA+ºî^°õÅÝ‹»]Mm÷ª¡¯ó\çílgÛ02Ð‘Óàñ½ØsÄ6~u~•„´= €c.ìŽ-c˜TuQt_§çlhzÐ”×­ zQÿC‰oX®nÆ_V2¹zvIýÌWÃ(ŠžÛ/Ä¿mW«Ê¥¡oÉÔåPlW«Ëÿ^™®4zOÇ} <PÄÆ÷JëÕÖ”†@¢­}o=ÊŸ#aÏ½¸Cy@Ï›ƒò†t¼gÖ0%wÎ|’)wdùÛ€ù¢Êž.¦‹*;[YþÇ]M·èf  0707010005c84f000081b40000000000000000000000015ab2175400000ecd000000660000000000000000000000000000003e00000000root/usr/share/doc/opensvc/template.service.ip.docker.conf.gz ‹      í\moÛ¶þÞ_A Üâ:^´û%Ár÷{é°tÛ‡¢ph‰²‰H¤&Rv\ÜÏ9$%Ê©cg‰»xï]ëåˆ<ÏsÞÈ#?þ˜ŸgÏÙ#~Pœ,‡Ì.KÁR\‹ê¡âñâWwÏÞËòùá‡g_Ó|ÏüŒŸ=Ãq_‹åBWéIûøTäc%ì¸ÒµpÅÁ#~PQ&Ñ%Ÿä¢yä»ªÆç°JüUËJ´cùç†Î”•žK#µ’jzÒ9“ŠŒ×¹m‡ßœ‘j&*i¹Jü“rÁ3vÎf‚§a‚ÁÔEåÎ›R$2“	\3
îMHAð“À—ºh¸"K–h•Éi]qÃ
C7,­tYÂ(™	ZÝ^³ ÉðáµÕá~‘²ÅL(®±<ÏÃ­ðž¦ ÑŒØ‡¤a	7‚aé[$®ä/„…qYÍ,(sÈ¸J(BûËÛ¡4×Ž`r_w b8å­áŒØr	ŠW2ýGyNÜƒ¿hµV¼ÝJº® -e:#u7ºBHÊ¼.&U©¬&ÝwÔ	ºo¾??\‡€,S1ï5ßj”)ªŒƒê°šé9(|1“ÉŒ½-…ºüã[¶y6±DÀÀ’kÂÁˆj.“Ø$e9bßrÅ&`ã2ËD%”eY¥ÃgJ§‚ŒîÕðO5„Gû'‘UÂ±HØà¼‡4€Ó$ÔOF¤„=	˜gZ¯Çïß;÷»K´¿ûåÒã\±7¿ÿLl+â(#„¶uŒ°wèœáÿ…6–ÕFduNt£k—ŒGÃ€A’·ÿ4	 •58Šï~ûÄ¥àÉáò
®­'à§ãðw.¯EN„îÌÐÎ¸ŠdÙ™†)xÝ €iP¤J9X@»±?¥Ñy^ðx®’sTC—Í4ž³SO:áVœ³9ÏkÐÈsä;‚8‡Ç‘SWbÇ)·7ÍY!z§)¤ÊŒë¾æð¸ÙC¦5aNÚÑ²ÿµÏ(F#˜hRÝTFXë`m”=Tr:°ŒLÍ©|äC\ccI%àPÊ:éG¦«‚[<Ì;5ó=ò²„®øÎ¿ÄùÕ*G›EHÌ±wsá#êúÊýM	
^‰ÒÆ¦Î2y³wdÙ•ï¼HS²Rgµñu
ÞÑö£þ>y!;«ÍÜNW`XeB$‰8Ñ€Ýs·?ð¹lo½N(„¥šPQÚRq2tyŒ$>RF;®K¼ƒ‡šã‹ˆe)›,é¦¶òqQ¹å8Ž¸¾ˆ„¹šBfÝz’ìkW©ø¡69cG‡#úß:ä?:ö°Ø?R"ƒt%B=C1y B½ÁÞd¨õaS„tÊG,Oà:b	^0Íõâ½ti
=‹qc	.:pFÇÖ%nJmöª÷3V¯†â\ÛuÌ˜m‚¹•‘‘¯ÃÌí´Ô•=ÿò&€ëùùû“Óæ˜tüC!Ê«äcºA;µ!¹DWÍ-×K”³ËßþpàHt.Œ‘ü1É	3J”vß,\È¦x’v…­ÃÚ\ïÁnzˆ æ	õ&8aˆ¸é²1KíÌ°ÒÚFõî‹»êO¯^”–
Hü[ãÆB¥|9b?!4ÔÅÒWOäŽo€ÛeB¤ô©‚ç{‘ÖE±Œn»U™HµºN…#}é’—;à …ÄÒ+Ä,¨ìtbÁùdTãÍ_­'‘Œã×¯Gî¿ãÑ!^~|ìV»:·}E·±¯^…øBê8‹î~½>È”ÓqRÖæßEÎ{ø­T”B¥¦ìRÀq9ELßã˜?tÝ¤‹†0âS|dD`Tî2ˆ&­g¨^¨Ñá_¤Íj©I—pãÝ`Å*®¦‚°ÃáÑð#¬'€¬³EæîÀõ¢Á¥¯Šü'ïp_p·àÆfâ{ˆï†øù:ï) ü?ŠJ‰<Ö—Œà¦±úˆS›*…%>:<~ÅœþiŸ‚Çî¾¬&ºÖ@²QûRD(Sû2$p,Õ@(Vð8#h)‹IÇz&E “Ü°Ð‡Á„' >g¥¨paKæ"¨cgCŽðÁ"gá­ú5©Ò¨u*žRÀ¾×G›Ø÷WuVO¾mýæ‘b´y† º£¢åØP„†Œ#¸ )Ô%¦+_¬Y-rå.b|btŽÕ¦_¨tû`¾ éŒë fkŒý ŽHÜð¢ÌÅIô“TõKè3dGàõ¸2¸š%ÈàS¿ãK£{~k,îF*™1¿«pú…L*H™Á@S« '8èMêtÄ^~ñ\ãeÁ7‚P‡Æ¯9î\//´»6æ<)ï¤ßQÇZc¬
+÷ä¿“ü[zÞ–å|Ê^PV{ôÒnB¡J¥q#wÞÖT!ú{-ó¼³ƒi0÷|¥âßÉIAúK¨é ö…!®ß%-ÇÀ M]8‰òá\2<yI¡<øPü[	0íÜ¤Y2zûöçH¤óØpÆ§ÀnqÔ'@eK?€jLà™#3õÄ,LãkZ’rZ‚èÑ
°ìhÔæ1kq¹iÇÉ¸{vß—ÝF~jOAßlõoå•PèF¸O–V_C‚¼Í:6 ã·a÷)”+»ÎÓ‡÷¨Ô4æ¡íBþ!}6°Ã,hå„õ¹/E>å /:ÎZ­ú€‰¾ìVØŽq‹î8c¯îBmÞõ“0êÿ d»¶l’Ô>Ùg_5”$ÂYWnx±ýbÍc€UÎF—0É¯¥/„œÎzz=ŠCø/dÁ×ìÍ[˜bÎ)—vÚ±?Ì Ò.„P€¥¼G ÐÈW°5L?Y:ïñÑ¢œw²@)ìÊØ;DmzK´¨ŸbY÷\yõcov¦ ”ø‰‰ÈÐCd¾ëqÂ“xûÐWÌÕb6KÝ+„^yÆÖ‚fùtÿ‚2”¨/Þx¹KÜà+5GØkBØE5‘¶â|MO´x8wèðC°:‚, l€v†±<Œ
¤\m#Wªl&èv—io)bÆçÄuŠq6PÚ=Àø\K(—¹Zbß$ú	øò)2É]—Cag348¼§6L/’ÅèÎT}É‰j4Ú³™5lÃ
DìŸ‡xŸ³Éµ}Ù¸%W3Úûœ¦HµîÏ»=¼ñïfŸf××¤g”
&Ý€ä6åVØµ‡‘¦–‰LEØl¡;“êÚ@¡ÍÌL×yJž¢¤@Üa ;¦„‚oì
®}SW—«èÿe$ø'ÐwŠ\Å·j§ŠnÝgò”‡ÓÌj‹Ô‰wwÞXü%íY/øÒ€`ì AWWàA¯®F®¨Ã~Öï¸øý§w®Ÿ`1Óyg×Ï?·«ýGñxoU3ãe>±PqG™±ºôþßP`DøÚY¨ª„­+˜m$ôŠ;IŠôäÊÔiÐÌ<)¦;ˆÚÛÂ¨®ÜV˜Á¦Ü“ëÅÉ•Ÿá(œ‡Ä»mll§EQKº)QðÂuäv íã#9í@ÜªÙU«wà6Ìn‘7,Œm°6]¢²øþ­é’QÐZnõm—*ñ{OÓ!ÿª!`"&T›ºMÒvÅp½w¡°ðÈó—^3êZn§öÅ*·.7ØðMê&‡Ë¸Ì©†¿-.×SìÚÅ[± Fç0ÇÕÛß]{;*º¥4ö}°´.J–É\¸ÕØ•áÁÉ€Œ[À¶.ëÓÉrïˆú4¹xá<¸ó©qüâB¼¢ÐàîÞTSèW¨æK¹€HÞm‚›ˆŽÜYpÕÔæe´¾	}'U˜W¥¬âŒ#à>Ä‚ÅõÐ¾@ iŒ–~@¨]–2ŠAñrI£E#Ü5–°/ŠìJiu@[¾)ºÏkÐ¤SA¡ÁCÃ%~Ù(’SèZù¶(ô½h6Nà}µÈò%Ž,ÃØ2±¯ˆ'ì;¨]ìêÝ£[ƒòs>ñÃ‹‡vË˜‚~68ö†¡{g0AØ“²—K×ÃL>šö—¯eÙÍ{·f’äØjc>M²ò§§HG5´”¹Ä¬Rç
_á™ƒäR¸×Nçí:Ú‚[2ßUGN“%\QâÞ .—áí &¿âÖ•½í¸ƒ^Ç©îŒzV}VEZ¿Ížˆa@üŒ»9à|ÓÞÁÿ4£°ÇŸî}§ŠwŸîÍxÛ]<Áuóûn&ø†b)0¹»X†O‘†.Ç›O!}”¹Åq\^üâ#RØTi_*ÁR»³.òüSÒü6²ÒEõnüséõuŠ]”ŠÞjsËþæáO¨„—=KwÁR·zàšËÛ×OÜ: ËÒüÛ#+µ:6œÈ[@šÖ,B©sÏhå{ÿîTËiÞ¡ªï9¡"X®—nŸ0vpÍKö®(¦çmÎœÄxŸ#ÝÎ^+mõÞC†ŸTÒõ‰‘`»›ßÞé¸¯»¼Õˆ}_Uºì\JM¯Tui#æ³“w/!—ø>KÞ½Ñã™ÛtðVðØ€@ÝC÷†·š1ÆK¹÷0»û]ÛÃMî1`ë`±ÙÜö³Ûá	™šß–xˆ™…þ†M&Öcõ0óz(TÛ˜•Þ¿òãIYU³±÷·J—[ÙTÔƒLêA8îj‚ •q_š}ÞÒl-pëÙÇ¾Jû¬UÚ8Þ«NëÀß£øy
¶íŒño™bág©Ý¶4Ã¿c„}i°Û2n;ãÛ¢8èBÝÃ¶ÓŠnKƒ»l}q·ãân[SÛ¾jèë¼×y[ÛÙ&Ì¢t~c}ïPÛbwþtþ”„t= €c!ÜŽ-O˜TµRÝ×é9Dš4åÂE+ˆ^Ô¿)ñËÕÍøÓJ¦ç/N©Ÿù|8^ºÄï®«UÒÒ¯dêrh,¶«Õå ü®LW½§ã (z¥ÍjkJC	 ÑÆ¾·ž?;åÏž°ç^Ü¡< çÍNyC:ÞŠ3k˜‰»g>É”[²B‡mDƒ;ù¢Ëž.;¦‹.Ÿ:["YþÌ¯…À
i     0707010005c850000081b40000000000000000000000015ab2175400000e90000000660000000000000000000000000000003b00000000root/usr/share/doc/opensvc/template.service.ip.gce.conf.gz    ‹      í\[oã¶~Ï¯ °(²‹ã¨I°Û‡l6hN/À¢—-šmû°X8´DÙD$R);.Î?3CÒ¢œõ%³¹mšè2"çûæFŽüìÙ.?ÏØ?(NVfç•`ãTì@Ü? n·º;ø «gÇ^Ó|ßàŒpÐ7b>ÓuvÖ>NÿÒJÀÉ£~PA&Õbñ´÷uƒÏaµø³‘µh‡ñ=/©j=•Fj%Õø¬s&9o
ÛŽüg7h&ÕDÔÒr•ú‚çì‚MÏÂ(ƒI‹Ú7•He.S¸f,Ü›’jà	&…+®„evâX"+VëÆ
¦Ä­e]1§ªƒ×Am \ÑÔð„£™0öäh´JÏ$Gñ²Wô§íµLê9x½Ðh·¶G²:Y¥UYebúj4œx
}Ê”ÊŠ:ç©S&ÓSQ³ÙD¦ö®êê÷oØL³õœYÍŒåé`D=•©ˆüš¬öWl$X&ó\ÔBY–×ºdH{¥3ð^?ê<Ú?)åFà‰HØá×xéN“P?‘%9	pçZ¯Æº7Ÿ%´¿ýùÊã\³·¿0žeµ0†éœpAï%ŒnêT¬€2Bh+P7À¸ö~"ƒKm,kŒÈ›‚èF×Î†ƒœM„ZAB hmŽâÛ_q™àå5\ÛŒ”€S~/ä(ˆÐÚ	W‘,;Ñ0¯ 0kR²Œƒ´›„ý!í„Îó’ÿ…çj9E5tÙLóà;çE¡SnÅ›ò¢Ý‰¢0A¾#ØŒ‹#Ç
&®Ä,Tx¸= ç-Á{¾5¦)3l*øþ™ÃâÌNì!3‰š0gíhÙÿÚç¤Z„ÑFZÃ£Ôr,²0v€X[ñ°ˆ\-ÇcQËÈÔœÊ2½Z¤06–Öe@öÖ\×%·x˜vn¦)"z‘œóª‚Ÿ%W|ç0}¸°Fh³©“9ôn.|`D]_Ù¢,qó\Ã”64MžËÛ½#ËcùÎË,#+uVË_§¡àým0êïSw²“Æ¬Àýè|	†e&D’ˆ£{Öûð„pä¦Ç7Š^'Â2M¨(m9ºüËc\ ñ‘2rØÁÁ›„]Fc,ËØhN7e:½÷º(ÜÉDŽƒN·˜„KreŽWáôÐ ûÆT˜º%ðý½ úÉqBÿ¬B¾/Ý–`§¢Œ"èR
„z.¤º ­NØÛµ>WuüwU4å®#–P‰RèÄ{éÒz„E!ÁåAüßÊJEÜVÚìT>î2V/‡âBÛuÌ˜m‚¹U‘‘¯ÃÌí¼Òµ½øò&`uª‹‹gçÌ1éøÇB”WËÆtƒvjCr‰®š[®—(gW¿þîÀ‘è\
	"ùC’&I•vÓÊå¸¢Ñ8@vUÁƒáIZ?öÀÐæj^rÓ{ð@0O¨/È0Á	CÄÍæ³ÔÎk­mTï>_Wzõ¢´L@âß7*Õ‹„ýD„ÐPK_=-$w|Ü.S"¥O<¯Øó¬)ËytÛÊ„ÜGGŽô…K.\î€K¯³ ²Ó©ç“S7}9 ¶žE2N_½JÜ§É1^~z: ÝeÝÛ¾¢ÛØW/C|!u¼‰î~µ:ÈTãaZ5æßEÎ{ø­LTBe¦ìRÀa5FL?à˜?vÝ¤³3`Ä)¦øÈ‰À¨Âe‹´ž¡z¡F‡ŸH›åR“.áÆ»ÁšÕ\;cÇƒ“Á)8>:EX=N +Xf)ÊÌÇÔ‹~—JL¼*òStœ¼Ã}ÁEÜ6‚š	ˆï!^ñ'òtÞc, "øµE¬/ÁMc)0ð§1T
s<|r|ú’9ýC’©Ý}Õt¬d£ñ¥ˆP¦ñeHàX¦ P¬à3pFÐR“6ŽôLŠ@*&¹a¡ƒ	OA|Á*QãÂ–,DP	ÆÎ9Â‹œ™h´ê·H•’Ö©xJû^lbßŸÔY=ù¶õ/˜G
ŒH º£¦åØP„†Œ#¸ 1Ô¦+_¬X-r.b|dtÕ¦_¨ÔTsú‚¦#0®ƒÂBXÂ¾G$nyYb€$úQªæ–¥t‡°£ðz\\Í’%dð™ËAbö»{zg,îF*™1¿«qú¥LkH™Á@3« '8èMê,a¯Ž¿ø®ñ²à/‚P‡Æ¯9í\/Ï´»6æ<)ï¤¯¨C­Ë!V…µ.zò¯%ÿ–ž÷’å³ç”Õž¼p…›P¨ƒ‡2iÜïÈwUˆ>ÆÞÈ¢è,ä`Ì=_©øwr2þj:H†}aˆëweEË10HÓ”ÎG¢|x'—OžS(>—e	EL»ðY,½{÷S$ÈÒyl8ãÿ¦ÀnqÔ'@es?€jLà‰#3ÍÈÌLã5-I9-Aô€hØ
v’´ùDÌZ\nÚÀq2îžÝ÷e·‘‰µ¿£ o¶‰úwòJ(t#ÜGs+Œ¯!ÁŽÞåG€ñÛ°ûÊ•Ç]ç‡éC…{TjsƒÐv!ÿ˜>Øaf´òÂzŠÜ—"Ÿr€Ø!ÁY«U0Ñ—Ý	Û1nÑoØËu¨M{£~Fý„ì±-›$µOöÙWeçH ‰pÖµ^lÿXÓØ#`•³Ñ%ŒŠ©‡3!Ç“ž^;qÿ…,ø†½}S,8åÒN»	ûÁ< íLøPÊ{ %¾‚í¬aúÉÒyoŒå¼+;Ùueì¢Ç;[§Þ-ªÄÇXÖÅ=W^}àØ;SGÊüÄHäè!rH&qãbÄÓxûÐW%ÌÕb6KÝ+„^ù†­Íòñþe(QŸøøâ1qƒG,Õa¯	5–°Ëz$mÍ:ø;4=Ñ60àáÜ¡CÂÁê²€°Ø	Æò0*–°+Û”A®T9Ø0LÐí.ÓÞRÄ„O%ˆëãìPi÷üCÆ§ZB¹ÌÕû¦ ÑOÁ—‘IîŠ¸
;›¡Á	Äà=9Äô"QŒáLÕ—œ¨F£}³@fÛ°ûç!vãóc6¹¶/Ú­
ÆŽ«í}NS¤Z÷ëúFoü{§Ù§Ùõu	éÙL¥‚I/@ò›r+ìÚÃÆHÓÈTf"ì¶P„Huc Ðff¢›"#OQQ î0 SÂ‘ÁÀ7v×¾©«ËUôÿ2üè;E.ãŽ[µcE„@·î3yÊÃi&EêÄ»;o­	þ’ö¬g|n@0v€ ëkð ××‰+ê°Ÿõ»ï/ûñ½ë'˜MtÑÙõóÏÄíj?Æ$ïêb¢a¼Ì'*î(3VWÞÿª Œvªja›fÛ¤XSÂ8ÏÒ2;»6M43MËqÍŽ¢ö¶0ªk·f°)÷ìfvvíg˜„ó0ƒx×¡í´(jI7%
^¸ŽÜ¤}|$§ˆ[5»nµâÜ…Ù-ò†…±Ö¦+Tß¿5]2
Z+À­~¢í\¥~Oài:ä_4LÄ„jS·Izˆ]q‡¸Þ{˜£°Cà‘ç/Xç²åvj_¬r›jƒØ¨Yäp9—ÕðwÅzŒ]»x+Ôè¦¸zû›koGE·”Æ¾–5eÅrY·»Ô!28âb qØVe]€b6šïQŸ&/w>5Žß\ˆWœÂaÜ†í,Ë¸€HÞm‚›ˆŽÜypÕÔæe´¾	}'UÀ—,(™^ÂGÀ}‰‹ë+ }@Ò-ý€P;¯d
ƒâå(’F‹F¸k,;Ä¾(²+¥Õmùfè>o@“N¥—øe£HN©åÛ¢Ð÷¢qäØ8÷5
 +æ8²cK’°¯‰gì[¨]ìòÝ£[ƒòs>óÃ‹‡vÇ˜‚~68öC÷Î`‚°'e/W®‡™|4í/ßÈª›÷n‹I’ckTŒAø,’•?<E:ª¤¥*$f:×ø
Ï$W‚vÎ•ó¼]G[rK&ð›êÈYd	7BT¸7¨«yx;h‘_qëÊÞvÜÎA¯âTwF=«>«"­ßeOD†‡0 ~Æz8ß´wð?Í(ìñ§{wàTBñîÓ½	o»‹G¸nžaßÍßP¬&wWËðYD¤Ëñ¦cHeaqW—?ûˆ6UÚ—J°”Äî¬Ë¢ø”4¿M€¬tQ½ÿœ@Žo €TìŠ TôN;˜[Fð7oB¥¼êYú,u«®¹¼}ýÄ­º,Í¿=²T«cÃ‰Ì°dÑšE(uî	ï¢µoµÐ»gP-gE‡ª¾ç„v ˆ`…ž»}ÂØÁ¡T.A®(¦çmÎœÄpŸ#Ý£½VÚ,Kê½‡?­¥ë#·"Åv7¿½Óq_ë¼UÂ¾«k]v.¥¦Wê¦²‘óÙIõKÈ¾ÏÒ£woôxî¶ÇwÞÐ¨{èv`xËc¼”{³»Ñõ¸=Üäv[‹Íæ¶ŸÝOÈÔü¶ÄCÌ,ô7l2±«‡™×C¡Z`°Yéý+?ž”U-6öþ¶Qéj+›êzI=§€Àz FØZö¥Ùç-ÍV·
‘mqì«´ÏZ¥mã½ê´ü=ŠŸ§`ÛÎÿ–)ö~–ÚmK3ü;FØ—[Æmg|[]¨{Øµ¢ÛÒàî[_Ü=rq·­©m_5ôuÞ#×y[ÛÙ&Ì¢tè5¸/ö<bk¿:¿JBº À±nÇ–§© LêF©îëôœFš>\”—­ zQÿ¶Â7,—7ãÏk™]<?§~æ‹A’$/Üâß®«U—ÒÒ·d6ÕÀXlWkªÃð½2]iôžŽÿ x Hmè•6Ë­)J ‰6ö½õüyTþì	{îÅÊzÞ<*oHÇ[qfS"q÷áÌ'™rGVè°h°–/ºêéòÈtÑÕSgKD"Ëÿý›u+h  0707010005c851000081b40000000000000000000000015ab2175400000b5d000000660000000000000000000000000000003e00000000root/usr/share/doc/opensvc/template.service.share.nfs.conf.gz ‹      í\]oÛ8}ï¯ PÜb7)f^Ò¦ØìN;Ó.v÷¡(Z¢lÂ©);^ìßsIÊ¢â&v»/ä‡Ö¶¤ËË{Îý"é<}ºË×“§l‡/g¦¼Cf—¥`*3·ÃÄíÖvO¾¸É>=þúä•›ïÍøÉÒ{&–]¥§íð%·S\8Úá‹ìc]òq.V#}ªjA*ñG-+‘®](+=—Fj%ÕÄ]|Çsã®¤"ãun[¥?hå.H5•´\%aœ\ðŒ½aSÁÓF	Á0_Qùë¦‰Ìd‚{&BáÙÄY#˜w|š
–ÕyN6a:cŸS(›X]-™ÕžE#<óÊÝrÆ2­o3¬.­éÛöÃ»o?&®K]Y2”2CÆYzÉºNÙX0#,4`/„M^ø{ÆŠ¼®äÆˆ” ¹Ð9¯¤	‚]\¥ 2ÿÝ •“Ë¤¬ÿ\ŒVPì
¤­Ñ!Á¥P©i'•àV\–²þÒùkÅó<× SÍe"HãDC(Œ¥J™VùÒyL ‘yGìïø—žÀö`ŸK2T¬âj"Ø);ž_ÒÇG/«yœ &}q;˜…(z0÷&ÌKQPiEXþ}O0ß\Âm#¸`À¥sòâ»!Æ×ZÍE) •Quàÿ‡¨”ÈeÙœçµ`¤5ë-¦2™â}Ísð _Ÿ¿üÙY8ô{ÅxD @s4Ö5Xc¤­9õ!ÊÔà‚£PàXªA¥-ÁÀºÊ“.M®"ƒäÈ½
ó£ë*#—Axñ9+¾QVæ¢1	èÊVäh^œî³„†U£‡µA%P
ìûådûþ¨µå=ù¶/X@
ÎR¨Pô$äµÃ'zO4iBÐ¤ÒuÉdÆ~ŠÀ¤ß„³uÏð±Ñyž¿T¡ùÚaM`§z¨x!¬¨Fì‘¸æE™£7 ‰~“ª¾f‰{…ÉÑ	¢Wð
•ËBZõE‡ý¥ëå|Mÿ é¡êbúaú…L*m45±	"q‚Ãñ`7©Óûåø§¿âž ŸH}A64^ºçeçŽHš]hoÌyGd¢¼—~wF½Ôº¸„Â¶ÒyOþ;É¿eä=gYÎ'ìÙ1eÁ“ç-Â%YÊ\4Æ¿'î|¬-arìLæ¹ˆá÷óÀWÄå,ÈI!ý9š]nfÆËçÖŠ¢´D:(iêÂGàHTHï.$»jœâpCé½,
‘JL;Š V÷5ÂÇ¿G‚¼’>bãJøLÜ(‡Çš¼ó	˜læ€Ô TcOE£™©Çfi0W4‹`%dd+`+ØÉ¨­'bÖ‚é'8îœ»g÷}ÙmäD‡Úo]Ò7Ûdýµºò™Tîã¥æy Qm>fG€þ s%'ð.È€ž	7âFð£òZE˜2æ†CÛ§üc÷ÚÀ³àe)„õ¹/E¾ / g­UCÂ¤X¶–¶cÜ¢'ÎØÏw¡6ïúQ8õ_²}{¶“ÔŽª¯mçX€D4ëÊ«ûC¬y¨ËÙÆùLêË…“iO¯„¿¡
ž±÷1Åœ»ZÚ[wÄþE`žH»BWòž  Qè`#V¬&ë®|c´\Í{² ”åÕá!z¼³õÚ-Ñrø„Úº¾èVÆƒùØñÎõìu¤,'Æ"£‘¡˜ÄtØ˜'³Ø—}÷W`®–ªÙ„ÖzÈ3v+h–O/)£E}öåëó}â†!nô~.s±ój,mÅ	:|F„U,ë°¨îÃ¡G"¨`uYƒ°ì”ry£¤Ø…Fw±’+UÆýnJ÷Ž;˜±˜ò¹„¸N3ÎJûñŒÏµD»ÌZ Kë	bù„˜äïˆÛ¡JÑ0CCÏÔf@åEÒd1÷ÍT½àŽjNÛ³2·°:qxb71?fïÄ¯±¬œV3Úç¼¥œiýÛ»wb‚óœeWWvåÇÈn2Ç›ÄJÚ²ÿ¶ã¬9úXk¥n8{ªª1*\zRØ®¶\´d¨d"ñ¿Ìâ8ÜiÁ¥š4ÚÌLu»:tê”ˆ; g§’p,šd üN\ÚÏÂ<n!AèèÿÏHðg ïywà¯u„ °*yT€ÓLkKÔ‰wwÞ[ÓÄK‰ œ/øÒ@0mÊBÐÕ"èÕÕÈ7u¨ü~}ûîüóoŸ†Ž‹©Î;»~aLjtÅú®uS}Y(,T‹²ºñß¸Àˆæcg¡ª¶®0Û:¡žzž&Ezzeê4Nhfž“Šöo/Ä›F«+¿†fØélqzf8j®cñ®C›Ûi¹¬%ý”\ò¢uäV‘vøHN«ˆ_5»j­â¿X‡Ù/ò6c¼Ío¾óÃ[ÓuNáÖ
¸™yÚ.Uög@þ§FÂ$L\oê7I’´Þ;ÈHØ <
ü…wÞôÜNïK]n]nðá!×«.ã2w=üº¸\#¤îÑpªBÏiõö³Y;C·”Îå¼«‹’e2~56&mÖVpÍG2qØn«º€b:^Q'Ï}÷15ÎßCZˆW.5xƒCoã”ö–¥\ “#Žu£ˆèÉ5¡šJóFF›è;¥‚?çÓ	éþŒÁCI‹?Wàör8‹¹¥µËR& š—£Hš[4¢]C°l VgÎ¯”VGnË7¥ð9ƒ%½	
[Â²Q$§Ðµ²¾-¢ØKÎ‘ÑÁ	z®V€,_’få–Ñˆ-xå¨qÊ~EïboNÑÝ:T˜óiP/VmÍ™ûlì+†œÃ4Â•¿P]ì]ŒvûË3Yvë^à¶š¤lµŠ1h^«båß"Ó h)sIUJçªNÜ*¦évÎ•¼Ý@[pë\à³êÈYU	3!JÚÔå²9Ê¸ª¯¸õmo«·Ð·qª;£žU?†U‘Õ×Ù‘á!ˆÇ¸›>6ü3üÝ³;*MóÊ½)§N­ÂÄ–HfHê)»Aêà¬TÜ]4X6¯UFúo>Aù(sKz\œ©ÙTYiâZI:užçß’¶	ˆ•>«wóŸÃi’J§"\)ºvÌ/#„‡7¤?¡^ö,ÝKýê­™ÂpÖÊWitâB\ßìÕéÀ‰LéÈêh–C©óŒ¯1[±´R[PY¥y‡ªáÌ‰ÛpËõÒïÆŽ¤r	{ø¦Ø·¹r—‡œéöuxÿ¼9WOÝªI*éÏ‰k‘Ðq·°½Ó	_wE«{[Uºv¾¤¦=¦ªªK…°PtðØp˜_Û£wôxæ·ÇwÞ<6 ¨{èvàx7+Æx)÷nw§ëq{¸Ëí¶›Ýí0O;<"WÛq³æ|Ã&ë±z˜{=ªÛ¸•>¼öãQyÕjcï»J—[ùTÔƒ\êA85ÜÔ˜Â*—}köc[³[»‘mqì»´Ú¥mã½ú´ü=Š?¦aÛÎ¿Ë{Hï¶¥~ö­Á~Û¸íœo‹æ uÛ^;º-î^°õÍÝž›»m]mû®¡ïóöÜçmíg›0‹*ÐË`ÁÃûaÏ~[L¥¦ä‰82‚þvGk~ÎCJBú3 À±~Ç–'‰ &U­T÷çôœ"KVíÂy+ÈýPÿº¤_XÞÜŒ]ÉôÍ³×î<ó›áh4zîþæ€ûìOµêBZ÷W2u94–Ž«Õå ù»2]iîw:áÏ `@‘Øæ¬´¹y4eE	hã¹·ž?{åÏ°ç^Üqu@Ï›½òÆÙx+ÎÜÂ”HÜ}8óM¦¬ÉjNØF4¸“/ºìé²gºèò±³%"#Ëÿ •ÑYW     0707010005c852000081b40000000000000000000000015ab2175400000497000000660000000000000000000000000000003b00000000root/usr/share/doc/opensvc/template.service.subset.conf.gz    ‹      íWß‹ÛF~¿¿bàä¨mì#yqHiZ8Úôú‚YIcy9iWÙ]ÙçÒ?>ß¬dkÝËÝåÀ¬#ï™oçûvft~~ÌçìœŽøˆ9ßfžÃÑÌñ¹ãÆîìCwØóéÇ3ÁzÃÛuÅ|pÙ(§ªŠ+LŽøH\|n•U¼÷öÞµ,Ž?µÚñ ã­ª|œiœ]k¯­Ñ¦œÌ¼TmäûmVìtP&ï=U¬–ô3­X;L85»nÞ7œë¥Î±¦dƒ½¹,Ë•)t¡ûù€–þüäÖ¬Ù%2káÊÄÀŸÏ1uµ$ÑV°°}D*8'å˜ø–ó6pÄû°“ª­)=v¬¸f"©šëŒâåmëröøzµßúºÇv³å"oZÿÃû—5÷óúBnØ~0œ;½‹¦>æÜ½©*»}n­sÄˆ¸*3m
²¦Úv,uÞÀ£„wB¿áWøMx–(O•½,*S2Íi:š.e`:¾Œ|v<N¸ŸLˆáDæ·#áµnKÆñží½KÆã5~*¹ÂÛ£äB¿‚ùÅSü…<¬Mà’Ýý°3H‘=XZ«
É\{j=#Ú¬t¾Â{‹Dº•áÙôòuñŸÐ•!• ÔŒ3ÛB5^‡VI>ßBQB½Æ
ÄA32«éàÓÌ >È.Åªäõ.±Oè=¡V 5ŒtÅ»@®´ÇîQ"pˆ’¶€áv8ú"‘J
ê{9{L}ŸZÔI|_›_„°ž)\~ˆBHÈS˜·‘Ÿ&b/°RPélÛ^Ò³„LHpŸV .œ6îQ™·Z†^¿°$Í Ý1˜Ø’þ æÀnBo‘ˆøVÕM…V"úS›ö–ò¸Ãh<CÖSèNZSéZñ.è@ý…êõú–n£à0mlRpüZçÎzÆ-|‚Ä+\<ÄMÛbB/§Ï~ÁšÞþ‰ú‹¬¹<X‘XÛ­M5…,’ï¬?\QÖÖ Îþx-ðw™yßÐ²R%=ŸJœ]HH—/C…öÝ»hçºÂy_co4úZ—ð»ÄzÕëu"½ug§€õ‹åo|g_…Àuûn€ômÝeàÃVZ\Ä”ÏÛXÊw9TÞu]s¡qìªRPÖõ××ïCÈ.cc¦ÿ/7xÛvuác„lÛŸ¥¬¦^ñ™4ÿ[c¼’SôQBõ@µ·L³ÉÐO¤ª…Ògh<^î“ºŸªn¯ÿåiÿ‹¾ÿšª§¯|®MÂ{¶ÅwæE/¢6Œ¯—ãƒ; ü³Ó%nW÷Á˜+ÏÿK~Ò>4Ê‹¤RmD¶»’?Ï#êðÕ4ÚÀØI"O•È—àß NÑÕ¾`J.»S¶SÞ’¯éÅC¬­O—ú»¸Ô?	eßúfGKƒç¾ûjñÙ™1D$§v¼ôþï„µN3‚|å<š²êFÛÅ†u¹:Éë(	áWtÁ7tu#V*öÒ]t'ô9‘aÃlÀOlyg hÒÁ&ªØ6Î÷ü¦lÅž÷ìì3£„[   0707010005c853000081b40000000000000000000000015ab2175400000e3c000000660000000000000000000000000000003f00000000root/usr/share/doc/opensvc/template.service.sync.btrfs.conf.gz    ‹      í]mÛ6þž_A$(œà¼Ž7—Þ‡mtïÚ ÁµÍÛÜ}‚]Z¢lÂ©’”½.îÇßII”w×–â—¬¯ò‡Ä«—!9Ï3Ã™!-={¶ÏÏ“gd§W"³Ê™•èÅíñâö«»'Ÿp°ÏÆŸŸ|gÇûÆøÉìøœ­–RÅuûZEpülÔŽdN')«úMO(ö{Á‹ïœÈ•\pÍ¥àbjO¾£©¶gb–Ð"5uŸ•ÂžàbÆ7TD¾”Ñ„¼%3Fã²ŒÀp™rçuÎ"žð®™2÷âÐI$Å‚©@~Êµ±Ú‚¦uÇ¯d¡"Ft1YÈ´È@fBÌŒYVàÊï@‰ çDÊ‡´£Ä?±–eþÈ´á‚èÄ‚Â6kT±¨Pš/ØWÕk¥¾Š­Îì‹¿TÄ<¦†é‹º·ä¿u;w>‘šX.S-a|yÊ#U#£¡›à¸QŒYx*íH®`2TMÙŸ›û÷ $$h ŠUn¿¶sC¡å8	ëVã´˜8©`’O¯£¼Ð'g8Qé GÌr&b]Žp®ó)rýöùóšm¤rI4SÓ ô8bZ#É„‹˜H‘®®5Tïˆüþ%T± `¨/¡Ú½%ŠŠ)#d<<¾Âã³Wˆ¦Ç	àÄƒ™±¬óp`‚z¥Z9“XþG˜;ƒ‹¸mp­g ¾‡x3Ä÷xP.›2Õ€ÿŸL	––%šÂdÉ5)4‹‡d9ãÑ¾4Àáóñ«×ÄéDÞB 4gY k47…õËCÂ„.€–Bžc±‚i°œÁ³Tntè°tè Ô¯µ9Gä7D#Ÿ’œÁaxÊJ•Œ¡Jr”Š¥%tC•ýÐ£Ú©xJû¾=ßÆ¾ßihO¾¶þóHñ)TèÐ‘—cÁw¤Ié‚¦J9á	ù& (X¹ ŒÖÞC'¢3áø’4Ìû>RkdåTÑŒ¦Fä8"vK³<eC$ÑÏ\·$²wè!9;¯GX…HyÆ¶1Lƒý¹Öc¸Öw#öCÙèÃÏx¤¤f` ±UˆcôÆe<"ßŽ¿ù®ñ²à/‚P‡Úõ¯yÕ¸"f–Ò]rÞ)ï¤ožQ¯¥Ì®¡ÃFÉ´'ÿFò·ô¼—$Ié”<ã,xþ w‰ZÐx(æÚ}Gî|(bîçØ9OS”VÉOàzêù
~9ñrbþbI‡žk'ŸÃ²Ü é “SMôÀ(?½[—-¯ìT^úPüÎ³ŒÅ†úŽÄdâb„~	¹N:güßhÁeçà¶rÞõ1PÙÊ¦@5$ðŒ•=ƒTL¯4ã;…×Ì0[¶Œœêx"d-0ý|Ç­q÷ìîÊnÍÿ`jÿd'}ÝfÖ¿W>ç"À}²‚,ñ…'QaÎ>$g ˜ßÅ§`] úQÍÖœ†9ÕH©m7åíg;ô’æ9 ¬§HWŠÜç ¯ :Jj­ú	}Ùi;Ä-¸ãy½	µEoÔÂ¨ÿ‚Ú²­¤ºe}vNG­\÷Bû/‰µ=f9[]Â$sy½d|:ëéµ‡ðwˆ‚çäýbJm,í´;"ÿF0/ H³dL >6ä=€F>ƒXQÖž÷ø†hÙ˜÷áBº6T¢ã}hÛ e3ñ)¦uK0X°ÅÆc^}àØá›['‚8’cá|Âô	“02¡Ñ<´e—ýe0VƒÑl„õ_\w"ßA3tzz“2¤¨Ï?}~qHÜ43k9‡«Ã%Vc#r©&Ü(ŠÐÁßX˜Gg‰µÄÃ¹C‡„ï‚‘d%Â`f8——½i#r%!»¨är‘€Ã -Ì±ó3˜	›Ñqdœ„tí]Hé2¬DàË§È$wE˜)™ÍQÞSè†Q9‹ÙC8Rñ’úƒ)%2­Cò•q¾žÏÙïÔÕX*cÇjF}ŸÓ”[¶_7/bzã?9Í>Ò%LÏ–¢T*˜t’wØ6¶¸°dPñˆÃÿ<	ýp#çb®!Ñ&z&‹4¶ž"·qƒhìNX9°Ø2 tí[–H}FÿF‚¯¾Sä:î€X­%ºuÉ[ <œzV¤N¸ºóÞèÒ_rpÂé’®4®K„—ÜÜ€½¹¹¤"¿zwùñçß†–Ë™L«~¾MTöqö÷Nv1“Ð_â‡¢ŒÌ½ÿ×6Ð¬ü³Q¨RÌ
7XD˜SB?/¢,¾¸ÑENhzeSEÎ4ù¾
š±·e¯nÜR¸0M.æË‹?ÂQyF®:Ôsc=,;kq7$;ya¹îHÝ| §îˆ«šÝÔZqîÂìŠ¼eal‹µÉ•EO¯¦kÂÖ
¨ž;ÚÚMmvMàq:äI˜0››ºEÒI¬÷6 yþ‚u®[n#÷Å,·È·ØðLŠ*†K(OmW\*a2ˆí­˜P£sX`õö£fI‘ZE×”NùxWd9IxÊ\56$mRGp–‹%É€Œ-`{(êãÉêäˆú8¹xé<¸ó©áü=ÄB¼°SƒS8ô[ÛN;KbÊ`&?Öôv@DGî¤tÕš—2jßŽ¾*L˜ËRÖqÆP@bÂâöØu’Vc¶ôBÍ*çP’—³@š-áª!°l ¬N¬]	)Îì’oŒîsšt*È$xh¸Ä—9™,„qiú^4Ž7Nà}… ÈÒö,Á¹e4"Kª,5.È»˜õ!º¦kƒòc¾ðÝ»vÇ˜JýlqìCOÎ`JaÊ^0®ì­¶ëËsž7ã^À­¤ul…1(?U°òO‘†j hÉSŽQ„Îªˆl†iWÎ…ó¼MG›QcMà£hÈ©¢„9c9®Ê|Uí+ã+j\Ú[÷Û9è‡8ÕQÏªã°*Ðú]ödØ…a›9à|ÓÉÁÿ8ga¿½wN¥LÞ}¸7£˜©)Ø
&3˜ÔcÜwS%9ÃàîªÄ²üT3ÒÐÅx‹)„<5Ø«Ë_ýŒT.ªT=±©$îÎºLÓû¤ùed¥›Õ›óŸHqÇ5HÅ]6½³Ì•üÍ[¦?&"š÷,=K]õ ëAº†ÐïµrQî¸`·ë¹:n8á1n©¶fY”÷¸³‹•ÚB•Eœ6¨ê÷œØK°T®Ü:aèàP*å —Ûö¶GNìú”gºCýRå´™eè„0ÂwûˆØ-‹p»›_Þi¸¯MÞjD~RJ*ÀÎ…Ô¸Æ¤T‘›À…ùè¤Çær.µéÑëŽMÜòøÞÁ[Ãcz uÝo=bK¹Ì®‹Ñõ¸ínrû€­Åvs;ÍÝÈÔü²Ä.fVîoØfb=V»™×®PU´1+yzéÇ£²ªjaï‹Jæ­lªj'“Ú	§Í@Mpc håºOÍŽ›š=ÜCˆ´Å±ÏÒŽš¥µÀ±SžÖ€¿Gñ8	[;cü"Sì!<JîÖÒ¿ÄûÔà°i\;ãk‘4¡îa;hF×Òà:ÁÖ'wNîÚšZû¬¡Ïóœçµ¶³m˜èµ×àéý°ç0ˆ-gÜ0Óˆi†Ï®ÀíhåÏyðQÜí 3æVli1ÀDB4NOÉ Ðô J.kAö‡ú·9þÂr}1þ{Åã·Ï¿·û™ßG£ÑûÌû·ÛÕ*3nìS2E>Ô·«ù |®LSšýŽ 4È"Sî•Öë[S*J ‰¶î{ëùsPþœ{:qÇÆ=oÊ«ãVœy€)¸.œ¹—)wd•;llä‹Ì{º˜.2ìl	H°qb‚ u%¢ëò—§Å–Ç”>„Z¬¸Ò}SQ-§Í:QÝ²ˆZã;®µ…®„9V}ö·«Éw\’-¥´4·´m7ÌB$ZþæÛ=Q®Çmc«õX~¾t†ó’Ú]à^owüÖio€ð_ßîöçÔ¸óƒÿ:X_Þ>Œo7ðÖðhizE»§øöàíbz•w5='¨­éõèíÃôvo.Ë²}Š~¤êJË%Ú–IúÚ"aâq
-mm;£Ø×\ŽTsé`†[ø{@ïñ;lù¥‹vÀ¯¯Ä¹ÓÁ[¥ò÷Àßcyœ¢L“ìˆe_Ÿ9j}¦›QnÍñïÇ¿òà¥šŽÙÈ¾jsÔªMƒì–ù÷œ£pºd ëü²ßJuØ­T•¢÷–Í{±ižÏŽzòž< æ/Þ‹ˆëH.{ñÖÉ°8ÎAõÜ9<wœ¦OÀó¬Qbƒ&ŠÑyO ÃÈ*úøÓ$Ä6ú¸”¥çÏáùã4}Z£Daª§ÐQ(„ª>5H±‰DÑŒÅÅ	¾pçPy³}Üílý·¨Ö™‚~ýAýûÐœÚFp}˜-Z½´(~Ëè%¦1#àHR½Ö´¼z$Lá{ÑxåÙ7äÓÓñøb<>ŸÃ¿?üí_ÉôtHžŽ_Ù£õGM/ÄÓÏDFo¯c–Ò£¼>£‚l/0Ÿ¿~}ÐwÆ…{¸~ƒ>¤X“Œ‹Â¸7_àK$ðí?øÒiÔ$¡‰¯¨œñhæÅÚ£GRó5¥ëO4¶Ï¹.‡Œ/zÀ—-‘«êµZøÊpF0|dvã{+Bhž§<ò<ôO:NÙ‚¥„Ncþ9Ê%ÜëV1	B¬[/óÂVË7l:£w¬¬=ÊyÐ$Õ v<Õ1|*"÷äPÛÜ>  0707010005c854000081b40000000000000000000000015ab2175400000e9b000000660000000000000000000000000000004300000000root/usr/share/doc/opensvc/template.service.sync.btrfssnap.conf.gz    ‹      í]mÛ6þž_A$(œà¼Š—u³A÷®)P\ÛäîCìÒe–D•”¼ëâ~üÍðE¢¼ëµ¿d}?$^½ÉyžÎiéÉ“}~=!{ü 8µÊÂ!)V9#ÓBÆJe4ßAÜ~{·_Ý=úŒƒ}2úòèG=ÞózÄaçlu-d4©ûÑ”Á‰³=~PI*9&¬jé£,±"ÙŸ%—¬îÂ/4QúL.Å’+.2žÍ&3‹i™u¯ÿ™>Á³9“¼ YhJÉ[2g4r½`Ì¤9¯rò˜‡pÍŒepo¨Õ-¨®¸Ðê ±aRF,‚/¤˜3‚úSsQ˜³… t)xC)XV@‡I(²8áa¡<~LYqÍXFRè9ÏK¾Z–dJ”2d*€fã2IÖšá¾°XÈ”t‰*òF•Ó¥HÞoðÂ·Þ¼‰hÁ
Ž“w74…'ž 8KèÏ"Y™ë_ŒÆßŸ^ž~Æ£Éè‡Éë ŠuÓçÄ\¹‰3¦ýoÊwâ[‘† æK&=ù	WÅ›®ç¼`*§!pˆåTRD¯#"&o:eÉÛ‰ƒ‰å( æ8 ¡–á8VÄÐ}OÄl:Æò“³è—‡D†g›1Ù ç#ØuJoxZ¦$+Ó)“ˆ‰Ó¿B0À¾)Ï4¨S âå&ç³Ë0/ÕÉ©ý^›è òˆå,‹T-8”È~™ÏÐ‡~Æ>iÚF’ˆk0
¹ä`Ðcp„ZéSžEDdÉÊx^ÓXª7 ÿ€	•Ìólõ%àmI"i6cdBFÃñði÷fq(ñÀf0S–ö`LP¯+’	xXþîG˜;ƒ‹¸mp©æ ¾‡ø~ˆ[ºÒ2™±Äu–,iRbøBJÅ¢!Îá¾—4ÀáñèÅ+bô_3B= 4gSQk/JŠÁÕ°L•ÀM!Ë±H A2• œÁ³4#¼é6Ð«ƒT·L¼h×OCŸœÁˆá <³*º’ŠîC‘à@B ´€nH×ÔNÅR
Ø÷z¼}–¢ =ùÚúÌ"Æ¤@P¡3@OiâqÀ'øiâ\ÐLŠ2'<&ßy`+·ä‚Ñê{èT‰¤†¿Œ±Âe ž,ŒéRHd@~GÄLà=DýÆ³ò†„ú5$gcðz4«ÈžBXmSà¹Ïþ\ ëùòV_ÌØ:RIy(…b` ‘òUà‰côÆE×£ï~‚k¬,ø…àÔ¡2}Ák^4®ð¤×Â\ës^)o¤ß?£^
‘^B‡ù“‡‡Oþ–žÇ„ÎÈÓÎ‚ãgèp—¨…‡"®ÌwäÎûR§vŽ]ð$Ai•|H1	µ|¿[9H6$Ueäcšæ’:©ÊÔx`O”ÞµK†–Wz*w>¿ó4e‡a'¶#™šáýûß=A¦“ÆcÃû7Z°ëÜææP•­ì`j T}cÄoz™•Z)Æ8
«%˜=`¶lu<á³˜>ÞÂqmÜ=»»²[ñ¿XƒÚïô¤¯ÚÌú·âÊ§<ó1+ÈÁŸY•ÅÙûø¬aÐ ³ä3°.Só	©bkÎÃ‡œ*¤”Ï¶™òGú³…êšæ9Ï@XO‘®¹Ë~ è(©µj'Lôe·¦m7ïŽsòê>Ô–½Q?£þBvhËÖ’ê–môUBÚ9e@"µ4Ýóíßké{Ìr¶º„i²àâòšñÙ¼§×^Âß!
^_ßÃªci£Ý€üÁœTeùñH‡¼c (°¬ÇŠj°ú¼Å×GKÇ¼›JTž¢£oQ„¥3Lë®Á`ÁW¦îªÕŽÝ­‘ˆ#y
~bÊbô1“02¥áÂ·e“ý¥0Ö£Ùë='òœl­ ³Ó›”!E}úùË³CâM¬ån59å…¤ü6Cg‰µÄÃ¸Cƒ„íB!<ÈªU0`@1Ç¹Üõ
¤äƒÀ7'—g1Ø0PÃI0o?ƒ™²9]r×HÆÉ ¦ýY·S`øX/Á—ÏIæ
?’"õš¢¼§T/B7‹éC8Òì9ÕTÓ½=¯Ù¼„¦{b|ÛÏ÷ÙïÌÔX*cÇjF}ŸÑ”[ÃšÌ9  6)ÖÿÉi¶:³/;†Ùãš°›X±·ä¿u;·}*4•­{$®3§TÕ Y‡­c+€Kö 9üÏcß7Rpž-$ÚDÍE™DÚSäz"n0 CÂ)s“‹4œk?·ãØ@›ÑÿŸ‘à[ o¹Ž;àV«	nÝFò(§š—RÇ_ÝùµPÎ_rpÂÉ5])œ„—\]½º
LR‘ßÏï~¹øôÛÇ¡¦Æõ\$U?Û&r}üþÞÊ.æúKl`‘E¾¨BäÖÿ+(æþlª$+J	£-CÌ)¡Ÿ“0&WªŒü	M-Ãt&ÉnÓX†zƒ†ëÕ•Y
¦Èdq=¹²#Üy¿êPÏõ°ô¬ÅÍôä…uäº#uóžœº#¦jvUkÅ¸³)òºÂØk9*‹ž^MW…®Pµ0´Õ{ÅôšÀÃtÈÿ0a"&:75‹¤ƒ$°Þ;ˆQØ xdùÖ¹n¹Ü³Ü2ßbÃC2-«.¦<Ñ9ümq‰€É Ò·bBÎa‰ÕÛOŠÅe¢]S:áà]™æ$æ	3ÕØµO.‚Ó\t$2¶€mSÔ(FÓÕÉõarñÂxpãSýù{ˆ…øLOFáÐo¥;m,‰(ƒ™¼¹Ån"rÇÎUchîd46Ì5B…)3YÊ:ÎØjHLXÌ¾½.àÈ¡5¦K? ´Xå<ŠAòræIÓE#\5–€Õ±¶«LdgzÉ7B÷¹ M¤<4\bËFžœT”YaÒ"ô½h1nœÀûÊ KVØ³ç–  ×TjjLÈÏ»ëC4M×eÇ<±Ýó»vË˜œ~¶8öŠ¡'g0NØƒ²Œk {í£õúò‚çÍ¸p«©[™ù¸O¬üÇR¤¡Zò„cÔ ¡³,C]ƒaê•óÌxÞ¦£Mi¡MàSÖSE	z÷…Aæ+÷ûÕ[ÜWªëcU¿ƒÞÄ©æˆzV‡UžÖo³Ç#Ã.ðÛ¸ŸÆ7üs¶øë{÷àT\ònÃ½9ÅLMÂÀV0™Á¤á¾˜:(Éw–îSÍHCã-g>ò¤À~|¸øÃÎHnQ¥ê‰N%qwÖE’Ü%Í. +Í¬ÞœÿŒ@Pœ@©¸+B‡¢·¶ƒ™2‚½yËôÇ²žÞNê“`©©`=HÕÚ½V&JÃìf=WÇ'<Â- ÕÖ,RãcÖb±R[* r%ªÚ='zB,+ïWÖÁ¡TÊA&)ÖímœØå)Ït‡ûM(ÒFø¡äf»a!nw³Ë;÷uŸ·
È;)…ìLHkLR–yá¹04ð¸¿„œUôèuGÆfy|ïà­á±=€º‡n†·1ú¥Üf×ÅèzÜv7¹}ÀÖÀb»¹æn‡djvYb3sû¶™XÕnæµ+TmÌJœ^úñ ¬ªZØûj£y+›êÚÉ¤vÂÉ!p?PSÜZ¹ìS³ã¦fÛ„H[û,í¨YZ;åiø{“°µ3Æ¯2ÅÂ£än-ÍðkŒ°O›Æµ3¾ÉAê¶ƒft-®l}rwàä®­©µÏú<ïÀy^k;Û†™^ZžÞ{ƒXý<²³ÛÏ#ÃGIp³ pL™Y±¥aÈ YfYóçô”<Mªtá¢¤¨“ã/,×ãßH½}úFïg~;‚à™~æ€þÛìj)/ôS2e>TnW+ó{®LSšþŽ} 4ÈÂÂí•Vë[S*J ‰¶î{ëùsPþœ{:qGÇ=oÊ­ãVœÙÀO\ÎÜÉ”[²Ü[÷òEä=]L‘?t¶x$¸wb‚ u•…—z¿ÓÉ±å!¥¾+®tßTTËi³NÔC·‡,¢ÖøŽkFm¡s0G²Ïþv59ÐáŽK²NJKsëAÛÙØvÃÌG¢¥¡áo¾ÍåzÜv1¶Zîóµ3œ•ÔÖèz ÷bx»ã·ŽH{„ÿzøv·?£Æ=˜ü×Áúzôöa|»·†GKÓ+óÈ<Å·oÓ«Ô¸«éAmM¯Go¦·3xkxtY–íSô#UWZ.Ñ¶LÒ×	{Shi»hÛÅ¾ær¤šK3ÜšÀßzßaË/]°~}%æÈ•˜fØ*•¿þËãeº˜dG,ûúÌQë3ÝŒrkŽ7þ=/Õt´È@öU›£Vm:d·Ì¿/àµ€ÓÅ Û Yç—ýVªÃn¥ªý°·lÞAˆûöàÙì¨'ÏáÉjþê½xž¸ŽÔé²oÛˆcTÏÃsÇhú<Ï%¶1h*]ô:<´¢O€?MBl£IYzþž?FÓ'@ 5J´`–¡z
…B¨êÓàPƒ÷‘(œ³¨<Áî*oÖ»¯?àÕ:—Ð¯¿¨}šQ[ ×ûÙr©äsýˆâç‘Ÿc Gâêµ¦î>XÐh fß‹fÀsgÏÉçÇ£Ñd4:áßŸ¾ã+™ÉãÑ}ô¥=Z”@ð2{üå^'‘Ò›Ëˆ%ô(¯Ï¨ ÛÌãW¯únÃ¨4×opàS†)V$åYY˜7_àK$ðí?øÒiÔ$¡S¯¨œópnÅÚ£}GRó5¥ëO4ÖÏ¹vCÆ=àË–È‡êµZøÊpF0|dvã{+Bhž'<´<´O:NØ’%„Î$c)Ëìs”ÌëV<11B¬[y/óÂVÝ6¿ƒÑÖÖå<h’jP;žê¾F‘{ô?‰sH÷cŽ   0707010005c867000081b40000000000000000000000015ab2175400000c84000000660000000000000000000000000000003900000000root/usr/share/doc/opensvc/template.service.task.conf.gz  ‹      í]]Û6}Ï¯ N°ÏLÐ¾L3Ag·-lÛ,d÷!(fh‰²‰‘HU¤ìñ~ü÷=—¤,ÊíŽŽù¡õXÒÕå=ç~‘”òüù>?Ïž³=~Hœåævâöø¸ýÚîÙ'ìó³_Ÿ‘¦·b1×UzÑÞ0ÑEÁUŠc'{üQL¢K>ÎÅòfªZÐJüVËJ¤÷”•žI#µ’jâþÈsãŽ¤"ãun[½ÑÊj**i¹JÂ}rÁ3ö†MO%ÃEå›R$2“	Î™…kgÜÁ$8ãÃT4&aV3q'’ÚB€bƒªVÆýæÒ§"­s‘B+ªÏGHVP|y&Ëtåèg˜4ì»3œÿìÛæ†—ìÔT³Óÿ˜Y¢x!þwšrËOMRÉÒšÓ1Onërd¦ÏÖB©2Yœ4üCñ\Â¶ Ë#{AÆ€A%ì'ÌE«-ûo{Xk&ªHƒ±Ö¸•êÐámÆŒ°Äº|È(n‚\yÄ‰3Ñ±<ª0ô	è.# z–ó	©ÇÆ‚ÕÜ!)WQéÚräòäü'•0VW"%ºÏeË6hu-ªJWGÇ„C¹öÕ6Ž-36§0[W
,gKBcØîJ8^7ç>àµ @Q~É ‘,JQ­àÃl>Š¼ŠT[zVƒç(Ž´‘·9Ap¸Jkës?\2žR­ƒ£œ\'emþ\ˆì7SQ
•šVpR	 p]N š}"íºUžë9¢d5“‰ aá7–p5­ò…,ÜÎDæ±¿á¿ŒW"Â«=…S 4Ú²Š#»`gÃóá+úáìäapœôÃz0Qô`L˜WW¦4Ä™îçèw‚ygp	·à‚×f
ñ=ÄCü™r‡ê—	¥žþ¿¤»|Y´¢žEÍ„JeÊAX&S|¯yàçó³W_3oÿ{‹ª8"  9ë¬1ÒÖ®b2¡L.8
Ž¥QÚÒ=8CG©º²&Žt¦3’
ó£ë*>î#qC!†\‘ee.“Pn_’£ùp"¸p%€†U£‡µA%P
ìûæ|û~«µå=ù¶/X@
ÎR¨Pô$äµÃ'¡ŒïD“&MP—T}	
.Ã
È…ÑºkøØèœŠ;Ï_H¢^ªû#Y%¯PÃ¡¤±ˆÄ/Ê?Hô“TõKÜfÈNÎõ¸‚W¨\ÒÒ½]?KÓ`=u+ºøIUcÐÃ/dRi#à ©‰M‰Ž»Ibç›³¯¾Ã9Aþ"!ôÙÐx]èœW3"iv®ý¹1ç‘‰ò^úÃõZëâ
ÛJç=ù$ÿ–‘÷Ê·…/Î(ž¿¤ h.É
†~J¥ñß‰;ïjK˜‡{+óÜµÍÇ5¨¯#ja½œÒ_CéäskEQºöJšºð8Ò»É¸óÂ¥ò&†ÒwY"•vIÙØ×ïÞý	òJúˆ#áoòàF9\Öä˜OÀd‹0¤ x*ÍL=6ƒa|K£VBö@¶¶‚Úz"f-˜~¾ãÎ¹{vïÊn#ÿ-:ÔþÁ%}³MÖ¿WW¾*Â}¼°Â¼$ªíÉ»ì¤ãÐd®äÞåæäXÂX	~T>”Ü¥bn8´}Ê?sŸì0s^–RAXO‘])ò¹ øÐqÖZ5$L7]ºš¶cÜ¢+.Ù×¡6ëúI8õ_²C{¶“ÔÞ9T_5ÚÎ± ‰hÔ•W/öÿ†X³8"P—³1$Œó[©¯çBN¦=½öþŠ*ø–½}‡!æÜÕÒÞº#öOó@Ú¹
ø¸’÷ B±b9Xw<à£åjÞuÈÒä;¯ŽÑ³½MÎn‰–ëÄ'ÔÖÍá°ðÅ…[ñæC`Ç7×³3Ô‘²@œ‹Œ"D†b’ætiq+öeßý«¥jÖMÎ;ô‘—l-h–OŽ/)£E}ñé×—‡Ä·Xé9ü<\æ,6bWÕXÚŠtø»³D<|8Ë$^«#È„`§”Ë­ mÄÞëB´r¥ÊàÃ ƒ9­àÞq3S>“×iÆÙ@iÿã3-Ñ.s…ÀÒ|A‚X>!&ù3âv¨ÒEt›!‰¡kj3 ò"i²˜û‰FªN¹£šÓör‰Ì¶Q"Ž/Bì'æÇlBâø9–¥³ÓlF{·”3­ÿ
ãfZ¯3lpþ£³ìÓ\É¾By6WQáÒKBÀvµà¢){@%‰ÿË,ŽÃ\*ÚÃ`™™ê:O]¤(]"î0€œJÂ±h’HšÐ¾a;tô2üè{C®â\àµŽÖC%ï€
pšim‰:ñêÎ[kšx)„ó9_.5ÁËnnAonF¾©Cå÷ý?^}üéÃÐQc>ÕygÕ/Ü‚G±¾÷º‹©†¾,nçÒR”ÕeˆÿÆu F4v&ªüº>HžPO	=/’"½¸1u'43KŠIÅN{Vøß4ZÝø¥0„0Ã.nç7a„£æ8F¯:´¹±–ËZÒÉ%/šGnioÉiñ³f7­Uü÷aö“¼ÍÄØ¦=#%‹ßœ®sŠfk•§íB%aMàiäh$LÂÄõ¦~‘t`!i@ó½ƒŒ„À£À_xçªçvz_êrërƒÙ¸^Öp—¹ëáï‹Ë5’Aê.¥†š‚ÃŒfo?‘Õ¹3tKi¿U©.J–É\øÙØ˜´Y[Áùý3d ã°­«º€b:^QŸ&¯|÷15ÎßCšˆW.5xƒCoã”6a×m›Z‰v ¢'wÖ„j*ÍmlB ï”
cá»”UœI
HjXü¾·.ÐÃYÌMý@¨]”2ÅÐ¼œDÒÜ¤­‚eÚyçüJiuâ–|S
Ÿ·°¤7A¡¡qJ˜6ŠäºVÖ·E{É92Ú8A×Õ
åÒ,£Ü2±9¯5.Ø÷è]ìêý­[‡
c¾êÅªÝs¦Æ>û’¡Gç0°'å/ïýžP£Ýúò­,»u/p[Ò¶ZÅ4Ÿe±ò¯@‘Žih\.©j@é\Õ~¯)†éVÎ•¼Ý@K[CÉ>ªŽœe•p+DIkƒº\¸¾?ž½åÖ·½­Þ>@¯Ý¸¨zV}yVEV¿Ïžˆa@|‡9àcÓÑÁÿ4³ðûv§ù>‚JÓ¼‡roÊ©S«0°’’zJûn:8+wï,›Ï2#}7› |”¹%=Þ_ý2R³¨²ÔÄµ’´;ë*Ï?'-,+}Vïæ?/†Ó$•vE¸RôÞv0?.Þþ„JxÙ³ô,õ³4dZÃ^+_¥ÑŽq·Ú«Ó†™ÒåÖ,‡Rç_c¶bi¦¶6 ²JóUÃž·á–ë…_'ŒIåöðM±»ßæÊI\s¦;üÓTá»gâç*ÂòN'|=­FìzNØù’Ú=]SÕ¥BX¨N:x<<…\jc{ôvGg~y|ïà­à±=@ÝC·Ç[­ã©ÜÜn§ëq{¼Ëí¶›Ýí8w;<!WËq³fÃ&ë±zœ{=ª%Û¸•>¾öãIyÕraïw;•.·ò©¨G¹Ô£pjx¨1m„U®ûÖìË¶fk[‡È¶8ö]ÚíÒ¶Àq§>­â—iØ¶sÆßåŠ=„_¤wÛÒö­ÁaÛ¸íœo‹æ uÛA;º-n'ØúæîÀÍÝ¶®¶}×Ð÷yîó¶ö³M˜Eèu°àñ=ØsÄæSi…)y"NŒ wWÐv´æqz•„ô{ €c!üŠ-OLÂ[´âö€"K–íÂU+È=¨WÒ–«‹ñ¯+™¾yñÚíg~3F/Ý;Üß~W«.¤uoÉÔåÐXÚ®V—ƒæ½2]i6zg"n(Ûì•6«[S–” ‰6î{ëùsPþ	{vâŽ«zÞ”7ÎÆ[qfS"q»pæ³L¹'«ÙaÑàA¾è²§Ëé¢Ë§Î–ˆ&&q!MžPß@/Ù}Ôš	Ø¼$Ô£ô˜N!)@aµÕ‚ÐF”:`Öá|j»V|¼z÷:¨{mÙ‡o'ôuÍAëšèäO³¬‰)ðP	þ•ƒ£cÉwû{aNÄ·U~6Ç»G\É×#pœ^¯Rº‡NSœÒî÷½å¿yñYGóOH˜…²üÏDEïPñ…gsô’óìÿ¶ú¶ù‚e  0707010005c855000081b40000000000000000000000015ab2175400000e72000000660000000000000000000000000000004100000000root/usr/share/doc/opensvc/template.service.sync.dcsckpt.conf.gz  ‹      í][oÛ8~ï¯ ZÜb×évö!½`²3-0˜Kètç¡(Z¢l®%RCRv<Ø¿ç”D9‰-Õ—wå‡6‘¥#ò|ß¹’‘ž<ÙççÑ²ÇŠÓ+‰YåŒÄ‘Žæ¹ÙIÜ? n¿º{ô'ûdüåÑ+;ß7åŒ=Â¡ÏÙj)U|Q ¾†ãg{ü †t$s:IYu£÷4Õ¿Qì‚+Vá7UØ/r%\s)¸˜^4.‰YB‹ÔÔƒþU
û3¦¸¡"ò7JMÈ[2c4.GÁÌ—)÷½ÎYÄÁ9S&àÚO‹¤X0È×ÌXmÁu‡ŸqÃtN#v¦YN5,&)×†È„ü@ý^*fïª‡„j¸ž	2Y3c$£‚N™ÀW k $‘ò>0üÉ= [ ù[éÝ+š1¢
ã%”h=#1e™ÃCH¦…™àn	|˜*1Ü¤Ì‚UŠÝXN¹ú?·Ÿ –Kò/-ÅY"UFMh#1·Ú¥Š33ÈÓLÄ	MDËBEŒPÐ€H8ZPb¶àp‚Uöˆ¼£Ñ¬–¸"Y7™Ñ³ÂZEƒa hks…p€øi‹­•´ÙéU”GÁ¶Dð´‚ûÂ¶ƒÆ,g"ÖµàH1ð{WùÍè3ŽùKü4•K°#å°R2bZ#É„‹˜H‘:èïä@õŽÈ÷ð/¡*´Êú°[G!ESF.Èxx>|Æg/,”' ÜfÆ²ÌÃ	ê•`‹B‚€Q€å/Áq„¹3¸ˆÛVpWzâ{ˆ7C|GlåÂ0›sðÿÄ”`i9X² iÁ×¤Ð,’åŒƒ.tASà>¿xIœþGäGAh@ €æl"`æ¦°þ|H˜Ð…rþºäX, B¼É¤ý–
Â=Þ
Hõk9F6 ˆOIÎàÄ–”•*º’Šå‡"Á„@i	ÃPå8||)ìûö|ûþ(¤¡=ùÚúÌ#Æ¤@Pa0@OD^Z|"?#MJ4U²È	OÈ7˜@ÁÊ­ ¹`¶ö:Ñ2-@†ã/Hª“À¦À@¦÷3±“÷àˆØÍò”‘D?sQÜÈ^‰þÙ9x=*À*DÊ3¨àÞÃ4ØŸK`=_Ü‹»Ç!Šlôƒég<RòP‰œTˆc˜ú€Þ¸ŒGäÛñ7ßÁ9^ü†Bð êP»±à9/gÒÌRºsCÎ["#åôÍõJÊì
l”L{òo$KÏ{I’”NÉÓ1FÁógè¸KÔ‚ÆC1×îgäÎ‡Âf×>ÆÎyšÚÚ±ü@uã+øåÄË‰Aú³!1TÏµ“Éz–$R™óÀ(Þ­K†;¯l(/}(þÌ³ŒÅ¦úÄeüáÃ/ 7Hç±áÿ;Zp98¸¬Œ; >*[ù9@h TCc}íF¦‹‰^i˜Æ+œ…×DˆV€-#ç£:ŸYL?ßÂqkÜ=»»²[ó?YƒÚïlÐ×m¢þ­¼ò)î“•aú™'QaÎ>$g€ñ™Ÿ‚ugD5[s~˜>äT#¥BnX´]ÈÛÏvè%Ís.@XO‘®¹Ë~è(©µê&ú²[a;Ä-¸ây¹	µEoÔÂ¨ÿ‚Ú²­¤úÎ>ûÂŽÑ„‰pÖÊ/´ÿ’X‹Ð#`•³Õ%LÒ9—WKÆ§³ž^{q‡,xN~ü SL©Í¥vGäŸæ i–Øp?Û”÷ ù
¶ÑOô“µß{|C´lÎ{²@)CÕé!:Þ[›·%Z¶ŸbY·ƒ[\a>æÕŽ~rÝ^lµgà'&,A‘@2‰ýà	æ¡-»ê/ƒ¹ÌfmÓ×¢WŠ|CîÍÐéée(QŸ~þòì¸­¯¦\V}zÔØˆ\ª	7
[ëø;xXÎ{?ˆ‡s‡	?#ÈJ„50ÀÌ0–—£i#òQBuQÉå"†	Z˜cæV06£âÅ8éî? t!9”ËT@	`°_/ŸÚ {FX)™·¢¼¦ÐL/¢2ŠÙC8SñœZªÙÑ¾©¹‡mX°Óóûñù!› ðN]¥2vìfÔ×9MYÕº·,Š:ã?9ÍVßìËŽ!ºñ8^V-ùw}Ÿ[†>‘n%ÖŒ=–KQ*LºÉ;l›[\Ø²¨xÄáž„~¸Q‚s1×Ph=“E[O‘Û@Ü` ;¦„V»eVïÚßøyÜ·MÁUôÿc$øo ï¹Ž;àVk	nÝgò(§ž©®îühté/98átIWv}W"¼äú<èõõÈuùýðîýå§ŸZj,g2m¬úù{‚ rŒ£p¼·ª‹™„ñŸX4ÖŽµ‘¹÷ÿÚV š•¿6UŠ™BÁl‹kJçE”Å×ºˆÃ€¦Q6UäL“×ð#î2x[ŽêÚ-…Óäb¾¼¸ö3•ßÃÂU‡:6ÖÓ²Q‹»)Ùà…}äz õí9õ@\×ìºÖŠ;pf×ä-c[¬MævqýôzºÖ(l¯€ê¹£­Ýûe×¦Cþ‡„€‰˜ØÚÔ-’H`¿w °ðÈó¬sÝrµ/V¹E¾Å†‡dRT9\ByjkøÛâR	Á ¶—bAÎaÝÛOš%Ej]S:åsà]‘å$á)sÝØ´IÁY.–$2¶€í¾¬PŒ'«“#êÃäâ¥óàÎ§†ñ{ˆxaCƒS8Œ[ÛA;ëwZkz; ¢#wRºjLÍKµoGßH&ÌU)ë8ã¨O ±`qû
ìº@I«1Ûú¡f•ó(ÅËY Í6pÕX6 V'Ö®„gvÉ7F÷9M:d<4œâÛFœLÂ¸²}/G‚'ðºB dé
G–`lÈ’*KòÔ.f}ŠîÖµAù9_øá…C»eL¥~¶8öŠ¡'g0¥°e/˜× öÖGÛõå9Ï›y/àVMÒ:¶B„”Ÿ*YùÝS¤¡HZò·Îá¦8UD¶Ó´+çÂyÞ¦£Í¨±&ðI4äTYÂœ±×e¾²uØ½¥Æ•½õ¸ƒ¾SÍõ¬:«­ßfO@†]Þc3œo:9øföøÛk÷àTÊâÝ§{3Š•š‚‰­ ˜APqß„Jr†ÉÝÇËòSE¤¡ËñSHyjp/õ©\T©FbKIÜu™¦wIóËÈJÕ›ñÏ	ÅI”Š»"l*zk;˜k#ø‹·„?&"š÷,=K]÷ ûAº†ÐïµrYî¸`7ëµ:n8á1n©¶fY”×¸³‹ÚB•Eœ6¨ê÷œØK°T®Ü:aèàP*å WÛûmÏœØÕ)GºÃíùd–¡Â?RÜí#b7,Âín~y§á¾6y«y§”T€K©qI©"7óÙI-›ù¥6=zÝÑ£‰[ß;xkxlA î¡Ûƒá­gŒa+·ƒÙu1º·ÝMn°5°Ønn§¹Ûá™š_–ØÅÌÊýÛL¬Çj7óÚª
ƒ6f%O¯üxPVU-ì}µQÉ¼•Mõ@ídR;áT"°¨	n­\õ¥ÙqK³{»‘¶8öUÚQ«´8vªÓð÷(§`kgŒ_eŠ=„G©ÝZšá×a_¶Œkg|-Šƒ&Ô=l­èZ\'ØúâîÀÅ][Sk_5ôuÞë¼Öv¶³ ½ò<½?ì9bËM¦ÃGIp· pÌ˜[±¥QÄ ÿ¤´°< ƒ@Óƒª\¸¬Ù?Ô¿ÁçwéõÅø×ŠÇoŸ¾¶û™ßG£Ñ3ûÌû»ÛÕ*3nìS2E>Ô·«ù |®LSšý;ÿ ¸!‹L¹WZ¯oM©($Úºï­çÏAùs"ìéÄ›ô¼9(o¬Ž[qæ¦âºpæN¦Ü’Uî°h°‘/2ïér`ºÈü¡³% ÁÆÀIëJDWv¿ÓÉ±å!•¡+®tßTTËi³NÔC·‡*¢ÖøŽkFm¡+aŽU_ýíjr Ã—dK)-Í­mgcÛ³‰–††óíž(×ã¶‹±Õz,?_á¼¤¶F×¸ÃÛ¿uDÚ ü×Ã·»ý95îÁüà¿Ö×£·ãÛ¼5<Zš^‘Çî)¾=x»˜^¥Æ]MÏ	jkz=zû0½Á[Ã£Ë²l_¢©»Òr‰¶e‘¾¶HØ£xœFKÛEÛÎ(ö=—#õ\:˜áÖþÐ{üÛ~éb€ðë;1GîÄt0ÃV¥üð÷X§)ÓÅ$;bÙ÷gŽÚŸéf”[kü»ñï<x«¦£Ev ²ïÚµkÓÁ »Uþ}ç¨œ.ÙÈº¾ì·Rv+U¥è‡½eóBlÚƒç«£ž<‡'¨ù«÷ââ:R§Ë^¼u2l#ŽsP=wÏ§éð<k”ØÆ ‰btÞèð²Š>þ4	±>®déùsxþ8MŸ Ö(Ñ‚AØ†ê)t
¡ªOƒCRl"Q4cqq‚/Ü9TÝlw;[À-ªu¦`\Rÿ>4§¶œVË…VÏí#ŠŸÇ2zŽeÌ8’T¯5-¯C†ÞÀ¦ð½h¼òÛ7äóãñøb<>ŸÃ¿ßýí_ÉôxH_Ø£õGM/Äã/DFo®b–Ò£¼>£‚l/0Ÿ¿|yÐwÆ…{¸~ƒŸ>¤X“Œ‹Â¸7_àK$ðí?øÒiÔ$¡‰¯¨œñhæÅÞ£GRó5¥ëO4¶Ï¹.‡Œ/zÀ—-‘Õkµð•àŒ`.øÈìÆ;÷V …Ð<OyäyèŸtœ²K	*Æ2&üs”K:¹×­b„X·
^æ…w-ß°éüÌÞ±f°ö(çA“TƒÚñTÇð5ªˆÜ£ÿ P0*¼3Ž    0707010005c856000081b40000000000000000000000015ab2175400000e3d000000660000000000000000000000000000004100000000root/usr/share/doc/opensvc/template.service.sync.dcssnap.conf.gz  ‹      í]mÛ6þž_A$(œà¼Ž7—Þ‡mt¯m€âÚæ€4×A°KK”MX"U‘²×Åýø›á‹DÙ»¶¿ÜúNþxõ2"çyf83¤ÅgÏùyòŒðƒâÔJDC¢W9#q¤” ù^âøq‡ÕÝ“OØÙgãÏO¾1ý}ã{üä	6}ÎVKYÄWuà4¿8à5¤"™ÓIÊª½£©bx¦`”¼`u~+Js"/ä‚+.Ó«Æ-1Kh™êºÑ¿JaNp1c×TDîA)£	yKfŒÆ¾Œ@YaÏ«œE<á\3eîŒVà	*‚+~ŸqÍTN#v¡XNªYLR®4‘	ùjú½,˜‘®†„*¢d²"zÆHF²b¿‚â)Rº»¸W¼Süo ÀJ¿N9DÐŒ‘¢Ø.B‰R3S–Iát¯¡¨úÀ"i©g£HŠNGºD4×)3 x±[AKÁçþ#CèpÁŠ@>¨»½© ÕLj¸xÁ#fÜ°˜ ¶uè^Ší€åÓ›(/OâÂ<*pU¨
¯@Å,g"Vµà¨`€ÅM>Eëø„mþÜ î:Måp(2Ðâˆ)E´$.b"EjÝ™{€Šê‘ïá_B‹µú ÖB_‚Š)#Wd<¼¾Âã‹Wˆ¦Ã	ÀÄƒ™±¬óx`‚ze±"B‚€Q€å/Áq„¹3¸ˆÛNp7jâ{ˆ·C|æB32ðÿƒ‚¥¾±dAÓ’®H©X<$Ëfð½¤)ð _Ž_½&Vÿ#ò“ 4  @s1‘%°Fq]R;‡„	U…Çb	àÚá$“æ,„kz| Ô¯”,‹ˆÌ0O#Ÿ’œÁ¡yÊ¼J€®¤"‡ÿP$8(-¡…o‡ÕNÅQ
Ø÷õå.öýQJM{òµõ/˜C
ŒH Bc€žˆ¼4øD ¿#M¼š²Ì	OÈW˜@ÁÊ­ ¹ ·æ:Q2-A†å/Hªc»¦À@†Ó+wàˆØÍò”‘D?sQÞ‘ÈÜQÇÅ%x=*À*DÊ3ˆZàÙx²?—Àz¾Øh‹½Û!Êlôƒîg<*$„—9¨ Ç(èËxD¾õ\ãdÁ_( •m^óªqE M/¥½6ä¼!2RÞJß>¢ÞH™Ý@ƒu!Óžü[ÉßÒó^“$¥Sò|Œ£àåt‚Ü%jAá¡˜+û¹ó¾4Q±cç<MMêç?	\O_Á/'NNÒ_‰¦j®¬|ª5Ër¤ƒFª2³8å†wã’áÉ+3”{Šßy–±˜C·S×Øç¯ïßÿ²´Î¸¿Ñ‚}ãà6?î€ú¨låú C ÓcÛ2UNÔJA7¾Á^8-Áè£`ËÈå¨Ž'BÖÓ/wpÜwÏî®ìVüOÖ öfÐWmFý¸ò9î“¤ˆ/‰J}ñ>¹hØ ´È\ð)XÈ€vFT±5ç‡áCNR*ä†AÛùcóÙÁµ¤yÎë)Ò•"÷9À %µVÝ€‰¾lcØqîxC^oCmÑõ£0ê¿ dÇ¶l#©~²‹¾JH;'H„½.lóBû÷ÄZ„³œ.a’Î¹¼Y2>õô:ˆCø;DÁsòÓ{èbJM,mµ;"ÿB0¯ H½ÄêßåØ„¼— ÐÈe°+ªÎšóß-ó>„,PJÓâü³t{Z&ŸbZ·ƒ[\a<æÔŽ¾™œ`=?1a	zˆ‚I,ÇOh4mÙfôUc4kjï=/òy4M§ç7(CŠúüÓçÇÄm½ä~]Õ×Qc#r]L¸.(Bƒ‡è,±öƒxXwh‘pMÐ2€Ì#¬€z†c¹oH‘²‹J.	Ø0tÐÀ`Þa3a3ºà ®‘Œ“öùB’CºL¤ ëøò©™Ø1W„éP!³à1Cƒ÷”j€áEäG1s{*^RC5ÓÚ72Íð@ÂÎÏCÆç‡l‚wjk,•±c5£¾ÏjÊÎÅ˜¯;æ4­ñŸf«3‡²cÝx÷+¶–ü»~Î†¡O¤„G‰5cåRx¥‚IW 9‡mb+€Kö 8üÏ“Ð7Rp.æ
m¢f²Lcã)r37€ÆŽ!á„ùÁ€ÅvöÔ¹ö7®­&°ýÿ	þè[E®ã¸€ÕB [w‘¼ÊÁ©f¥Fê„³;?iåý%'œ.éJà\"¼äö<èííÈ&uùýðã»ë?ÿ64ÔXÎdÚ˜õsÏA¾£°½ÙÅLB{‰,DŠÒ2wþ_™@1ÿg£PU0]ÐÛ2ÂœÚyeñÕ­*ãp@S‹(›äB‘oá+Î¿õ­ºµSaàÂ¹š/¯n]Gþ<ô œu¨ÇÆº[fÔâ¶KfðÂ:rÝúñœº!¶jv[kÅØ„Ùy}al‡µÉ•EÏ¯¦kŒÂÔ
¨š[Úš%ZfNàq:äJ0“›ÚIÒI¬÷6 9þ‚u®[n#÷Å,·ÌwØðLÊ*†K(OM¿).•0ÄæVL¨Ñ9,°zûQ±¤L¢kJ§|¼+³œ$<e¶’6©#8ÃEO2 cØŠº Åx²:;¢>N.^[n}j8~±/ÌÐ`íV¦ÑÖÁºTàÇšÞˆhÉxW¡¹—Qû&pôPaÂl–²Ž3¶€º »®ÀÌxr™ÒÕ«œG@1H^.i¦h„³†À²°:1v%¤¸0S¾1ºÏ9hÒª “à¡áW6
äd²Ú¦Eè{Ñ8\8÷• KWØ²Ç–Ñˆ,ia¨qE~€ÜE¯wÑ>º6(×ç+×¼°iÆäõ³Ã±W=;ƒñÂ•½`\Ømæ—ç<oÆ½€[ÕIãØJbà?U°ò»£HC5´ä)Ç¨Bç¢ŒLºifÎ…õ¼MG›QmLà£hÈ©¢„9c9ÎÊ|eòþ°zKµM{ëv[ý§š=êYuVZßdO@†}>c;¬o:;øç(ìð7÷À©øäÝ…{3Š™Z[Á`ƒzŒën`è $gÜ}ðXúO5"mŒ·˜BøÈSíøpý«‘ü¤JÕ“Jâê¬ë4½Oš›&@VÚQ½9þY 8‰RqU„	E7–ƒÙ2‚»yÇðÇDDóž¥Ç`©­`=HÕºµV6JÃìn=WÇ'<Æ% ÕÒ,ƒRãcÖb±R[* ²ˆÓUÝš3a–Ê•'J¥ôa“bó¼Ý‘»9ç‘îX¿°¸mf:!Œð£‚ÛuDìŽE¸ÜÍMï4Ü×6o5"?…, ;RãSQ”¹\˜‹NxìXÌ/•îÑëŽMìôøÁÁ[Ãcz uÝo=bK¹Ì®‹Ñõ¸íor‡€­Åns;ÏÕÈÔÜ´Ä>fæ×7ì2±«ýÌk_¨*Ú˜•<¿ôãQYU5±÷ÅF%óV6Õµ—Ií…“G`;P\Z¹éS³Ó¦f÷"mqì³´“fi-pì”§5àïQ<MÂÖÎ¿È{O’»µ4Ã/1Â>58n×ÎøZ$M¨{ØŽšÑµ4¸N°õÉÝ‘“»¶¦Ö>kèó¼#çy­ílfAzã4x~?ì9bËm¯ËÂWIp» pÌ˜±¥QÄ ÷´0= ƒ@Óƒ*]¸®™êßåøËõÉøo¿}þ­YÏüv8^˜w˜¿íªV™qmÞ’1(ó¡Ò¸\­Ìþ½2Miæw:î5 ð@i¿VZ­/M©($Ú¹î­çÏQùs&ìéÄô¼9*oŒŽ[qæ¦âºpæ^¦lÈò+llå‹Ì{º™.2ìl	H°u`‚ u%¢³ÞéìØò˜Ò‡P‹Wº/*ªå´™'ê¡;@Qk|Ï9£¶Ðy˜ã¢Ïþö59ÐážS²^JKsëAÛÛØöÃ,D¢¥¡áo¾íåzÜö1¶Zþó¥#œ“ÔÖèz bxûã·ŽH{„ÿzøö·?«Æ˜ü×Áúzôa|û·†GKÓ+óØ¾Å·oÓ«Ô¸¯éYAmM¯Gï¦·7xkxt™–íSôUWZNÑ¶LÒ×&	{OShi;iÛÅ¾ær¢šK3Ü™ÀßzßqË/]°~}%æÄ•˜fØ*•¿þËÓeº˜dG,ûúÌIë3ÝŒrgŽ?þ=G/Õt´È@öU›“Vm:d·Ì¿/àœ´€ÓÅ Û Yç—ýRªã.¥ªý¸—lÞCˆmkð\vÔ“çøä5ñZ¼@\GêtY‹·N†]Ä±ªçÎñ¹c5}žg»4)÷:>Œ¢Ï€?MBì¢MYzþŸ?VÓg@ 5J´`–¡z
„B¨êóàPƒÛHÍX\žá†;ÇÊ›Íëngë/¸EµÎ
h×ŸÔí‡fÕ6‚ëÃl¹TÅKóŠâ—±Œ^b3Ž$Õ¶¦þ>¨é< aî‹fÁógßOOÇã«ñøb|	ÿ~÷·KÜ’éé<¿2GÿêŽê^Š§Ÿ·:‰ŒÞÝÄ,¥'Ù>£‚ì 0_¾~}Ô½ãÒ¾\¿Á_R¬HÆE©íÎ¸‰îþƒ›N£&	HÜ¢rÆ£™Ekn¤æ6¥ëo46ï¹ö¯CÆp³%ò¡ÚV·¼ g}ÁWf7öÜ[Bó<å‘ã¡{ÓqÊ,%tZ0–1áÞ£ìéd·[	Ä$°nlæ…Oõ;lZ½·¬¬½ÊyÐ$Õ v<Õ1ÜF‘{ò m Ú     0707010005c857000081b40000000000000000000000015ab2175400000fce000000660000000000000000000000000000003d00000000root/usr/share/doc/opensvc/template.service.sync.dds.conf.gz  ‹      í]mÛ6þž_A$(œà¼Ž7—Þ‡mtû×66é}‚]Z¢mÂ’¨Š”w]ôÇßÌ”(ïÚ–ã—®ïd×–Fä<Ï¼qhéÙ³}¾ž<c{|¡8½È¢>3‹\°8Ö;‹ÛãÄíWwO>âdŸ?=ùŠæûgüä	{&·ªˆ/ê«ë"‚ÏÏöøBíèHå|”ˆêB?òDü¦”²õÞ%}‘j.µT™Ì&Sb1æebêAÿª2úBfSQHÃ³È](|ÌÞ²©à±…`0_QØïu."9–3œ‘Và
:‚#~S23š™)ž$'2cjLéŒçzªð;3Èq#Ø¸Pé $|J-•Z¥åX›chÙëò1+ù=/&Â°±Lð,6JT4ƒïæ2ö.708žØ÷v<¤qPï  '0¿TÄ’{Pø‚ÀÉŒ‹³X$†Ãç‘sVL«²ˆËT,à¤{?•úž´´Ô†Mù\0Î´üÐ/s˜ŒþÅò„ÆD¢} w=ú†¦üÿmfŒE<‹ev£Ý%Íþbq‘Ó[:HesQƒH$šNÀŸË(¹Dç<)á|^Öó"z}Ö³oˆ[ÊLûìv*áš9ÄY€µ9/`0qÈ,‡”ÙjZi¦²A ì[Ány‘Á Ì”¦AŒýZß!
ä6œ|
ä³[i¦ª4Œ‚zÚ¨üÝ÷u™£®ÎH‹Q"`*2IØ¨|fEÃqÓ ü“£Ùô”,äf)^	þ-5ð6YÀ„´<P0=¯6 n®Lb¡a£ÐÒJmO©½Ã1æVˆÌi§ÄxiTŠšõSMlÌe¢ a2§7öì•
œï5âßk@Õ7À‡:çˆxœ¿À(eãÕ€ý”±_¾í³B•YlË§€%LÜ‘™ ‹Ä	À,™§p¸Jô€]¡×»ú"à¢
Ç¾ËEvõûwp| ä<2ÎP˜3Š‘t‚žGN­f‚Û|È¿dÞ‰…9KÈ{Ã<&†S°hÊ³	|BìCá–¶xB¢²Iu| léÌhZf3MŽÅîw•”©`ÐZ®½PhF›‚‰Ö89r'Œ'·|“™©‘E6ðt]òÉu”—úoÍ
öNé-¢A,€±®[j\çÍ>â˜?5ý~’¨ÛÊ›Âˆ$ðñO¬›vWCœA½öücDèú«C¸£ÄŠÉÁ.Ø°Þ…Ï^!œ' ?Xf*ÒÌÃ	êU`w(ÂÈûKð9¥Û‚‹¸mp­§ ¾ƒx=Ä$p[‰‰(ðÿ[@²”øÁÚLÎf)"¦„-šV||>|õšYýS„3&€æŒäê¦ty›²ô+V@Ìjà{è[ž1iÂì„®‘¤bªä’|¨ÀÙGS("£X½8•`fW‘Ã¿8Ü.EÑÇçBƒÚ©8Jû¾<ßÄ¾?JexG¾¶þsHñ)(G‚!"äáCY,¼GšxD9 “cöE &P°r+@.˜-ÃGaù‹E!d³0 {YXu¤Â@Ì~ÄzòŽ§9¦ö@¢ŸeVÞ±È¦!}vv^g`Y"S¨ab›ê„ìÏ°^ÎïÅžˆãÈÊtôƒé§2*”` ±U¦pŠ](‹¾~ñãd}ióLü u¨íXð˜W#ÂäVÙcCÎ‘‘òVúúˆz­Tz6…J:ò¯%KÏ{ÉÆ	Ÿ°çCŒ‚ç/l½*2Ô‚Æb©í{äÎ;¨NscgP|¢´JþŽçŽ¯à—ÇNNÒ_ô¡ÔÕ3måscDšS¹ƒÔ˜k£n¬¼Ð%È%Ã•Ê½Å÷2¥Å#7ªChï~	ÙAZß¸¿Ñ‚ýàF‹*î€ú¨láæ ¡PmTÐÂL—#½Ð0¯¨n¶Z‚èÑ
°ì|Pç!kéç8NÆÝ±{[vÛ€Ú?PÐ×m¢þ½¼ò¹ÌÂÂx5êG¢Òœ½Ÿ5l€áª…Š¬dÀ8#®Å’óÃô!ç)rƒÐ¶!H¯ìÐ·<ÏeÂ:ŠlK‘‡à@ÇY­U0Ñ—ÝÛ!nÁoØëu¨Í;£~Fý„ìÐ–M’ê+»ì;#\PÂYvx¡ý{bÍC€UÎF—0JfR]ß
9™vôÚ‹Cø–P?½ƒ)&œri«ÝûÁ¼¨–¸Ï‡”òž@WÁ¬¨&Kß;|C´(ç]…,.Ëóâô®„ó3úCmÐ¢J|‚eõB&Ê«a¿Ï÷ø˜‘)®l‹1zˆ1$“Ø‘ñhÚ²­þR˜«Ál6ÂõBÏ‹|ÃV‚føäô‚2”¨Ï?~zqHÜ´XêØ¹u¸1ilÀ.‹‘4.£ãßØDgéLÖZ$Ü«ùaßmªGÒìJ¥¢–+³1Ø0L`Ž0ï°‚‰)ŸK×(Æ±…h¯ßc|®$”Ë<[`W}ê" “ìa9„æú2Ô‰ÄsJÝÃô"òQŒ>Â™f/¹kƒM0©ðÈ¬jƒArœ&ò#ôù!› ðNìKeì¸šQŸg5e›1ôv}'ÆÿÉi¶úæPýy-û«¾Î=C)—Ê–Œ=V·™W*˜t’sØ”[\Ô×¥Œ$v¨Ç¡n”à›uà\[=EN¸Á 4vÛ³öÁ@ÄÄ ïÚß¸y¬Ú c+úÿ1üè[E.ã¸€Õ!Ð­»Lž€rpêii:awç'£½¿”Ú·[‘+„—ÝÜ€½¹Ø¢2¿ïøñòÃÏïûDÛ©J]?wMäÇ8Ç{¯º˜*/s‰E‡¢ŒÊÿ×Tháÿl,TÂ”Ì¶Œ°¦„q^Di|q£Ë8hz¥“‚iö5¼Íx*ÞúQÝØV¸0Í.f·7n†ÿ=Ì ì:Ô±±žE-i§äZàý` õå9õ@ìªÙM­ûÁ}˜í"¯_Û`mÊmó:9s#£ µ®g–¶´s†zÓ!ÿ¦ `"&á~)’h‹ToŒÂzÀ#Ç_°ÎeËmÔ¾Xå–ùî³QYåp¸å‡jøûâ5Áx*Ôèpoû Å¸LHÑ5¥9Þ•iN{íjlckQÁ=É€Œ-`[•uŠñhqrD}œ\¼´ÜúÔ0~÷q!>£Ð`ãÖ4hë`YÌDr&—¼Ñ’{ì]5¦æ^Fí›ÀÑ7R…‘°UÊ2Îv—žË¡`±û
¨/àÉA£¥j9í­‚â%Ü\H‹FÒn«ëáæS²«LegÔòÑ}Î@“V©Â½ÄÚ/rRUfn«ú^4Ž1nœÀópƒW‘,pdcŒ-ƒíADj\°ï¡v1ËS´—®ÊÍùÂ/Ú=còúÙàØ+†žœÁxaÊ^0¯ìÉGSy&ófÞ¸U“$ÇVf!þU%+ÿqi¨’–<Ám€1î-#ZƒiRç<³ž·éhSnÈ>d9U–0"ÇÞ ÊÕFBŸ_qcËÞzÜÖA¯âTsF«ŽÃª@ë÷Ùa„×XÏë›NþÇ…þtîœŠ/Þ]º7åX©0±3ê1î»ÐÁY.0¹»òXúW‘ú6Ç›O }”‰Áq\]þê"’oªT#¡Rwg]&ÉCÒ\› Yi£z3þY 8…RqW¥¢÷¶ƒÙewò†ð'²ˆçKÁR»z`ÔPAèöZÙ,w\ˆ»åZ7œÈ·€T[³¥Æ96Ç¬ÅâJm©ÊYœ4¨êöœP‚–¨…í†¥r	ú°E1]osæ$®O9Òê§— Í4¥-þáG…´ûˆÄˆp»›kï4Ü×:o5`?…* ;›Rc©(ÊÜ.Ìe'<6læWÚtèmÛöøÞÁ[Âcz uÝo9c—r·0»mŒ®Ãmw“Ûl,6›ÛiîvxD¦æÚ»˜™ßß°ÉÄ:¬v3¯]¡ª0hcVêôÊGeUUcï³Jå­lªj'“Ú	'Àz èÎ •ë®4;ni¶¸Uˆ´Å±«ÒŽZ¥µÀq«:­‡âq
¶vÆøY¦ØAx”Ú­¥~Žv¥ÁaË¸vÆ×¢8hBÝÁvÐŠ®¥Ám[WÜ¸¸kkjí«†®Î;p×ÚÎ6ad ×Nƒ§÷ÃžÃ Ü<¯ºc^õs¼•„´{  ÇTØŽ-§ô±¢Ì²æÏé9ëšîUåÂe-ˆ~¨—ã/,—›ñ_2~ûükÚÏü¶?^Ð=èo»«U¥ÒÐ]2zeÞ×·«•yÏßW¦)~§ãn tG<+kykJE	 ÑÆ}oÊŸaÏVÜ¡< ãÍAyC:nÅ™L	ÄmÃ™™rO–ßaÐ`-_TÞÑåÀtQùcgK@‚µ	’V¼¬¿Ãîi±å1•¡+®l¿©¨–Ó¦OÔA·‡*¢ÖøŽ=£¶Ðy˜ã¢«þv59ÐáŽ-Y/¥¥¹u íll»a"ÑÒÐð7ßöŽrn»[­GÿúÜç$µ5ºÀ½Þîø-#ÒÞ áŸ¾ÝíÏªqæÿla}zû0¾ÝÀ[Â£¥éÙ…tàíhz•w5=+¨­éuèíÃôvo	mÚ²]‰~¤Õ•–-Ú–EúR“°Cñ8-m›¶[£Ø­¹iÍe3ÜXÀ? z‡ßa—_¶1À-ðëVbŽ¼³…¶*å€¿Ãò8‹2Û˜ä–Xvë3G]ŸÙÎ(7Öøãßyð¥š--r »U›£®ÚlaÛUþÝÎQp¶1È6@Öõe·•ê°[©*E?î-›bÝ<Wuä9<y@ÍŸ½/·%u¶Ù‹·L†MÄ±ªãÎá¹c5}žg‰›4*Ÿu:<HÑ'ÀŸ&!6ÑÇ–,Ï«é Ð%Z0—¡:
…B¨êÓàPƒëHME\žàwU7Óín§Ë7¸EµN×ŸÜ=Íªm Ç‡Õr©‹—t‹â—±Š^b3 ŽŒ«ÇšúóP áwp±(ð¹h<ÿíöñépx1žÏáÿßüëÉô´Ïž_Ñ§ÿtŸš^fO?­u)¿»ŽEÂòøŒ
²½À|þúõAŸm—öæú|Èð&Åš¥2+}ò>DŸþƒFM2>RøˆÊ©Œ¦Y\{tÏHj>¦tùŽÆtŸk;d|Ð>l‰]UÕÂG^€3‚¹à-³ÏÜ[€Æó<‘‘ã¡»Óq"æ"a|R‘ŠÌÝGÙÓÉ>n%3Fh€u‹àa^xUÿ„Mëï`ö–5½¥[9÷š¤êÕŽ§ú£ŠÈ=ù/l²j’    0707010005c858000081b40000000000000000000000015ab2175400000de7000000660000000000000000000000000000004000000000root/usr/share/doc/opensvc/template.service.sync.docker.conf.gz   ‹      í]mÛ6þž_A$(œà¼Ž7—Þ‡mtïÚE_r@š»A°KK”MX"U‘²×EüÍðE¢ì][Š_²>È¯^Fä<Ïg†´øìÙ!?Ož‘~PœZ‰hHô*g$–Ñœû‰;àÄVwO>bgŸ?=ùÆô÷ëñ“'Øò9[-e_ÕÐ´˜2§.øA©Hæt’²êY¿%Ãû£ä‹7Nä…\pÅ¥àbjNþHSeÎÄ,¡eªëfÿ&…9ÁÅŒ\S¹ç¤Œ&ä-™1ûF0=f…=¯rñ„GpÍ”	¸7ÂË"*bSÍ”{„1Sä/¹ùj.’bÁŠ )WÚhÚ§"8þ=Sšª¡N‚Lˆž1Ã¿\úÕ6€b¥>€I>½‰òR}QP*Ý
•pÄ,g"Vµà¨` ÎM>Ñä#¶ùSCñ×i*—D±bÁ#†-Ž˜RDK2á"&R¤+ƒ}‹	ªwDþÿZ°À¤ëK¨2 CkIAÅ”‘+2^_áñÅ+DÓápâ‡ÁÌXÖƒy<0A½²XY“XþG˜;ƒ‹¸íp£f ¾‡x;Ä÷xP.4›ÂØÂÿ3+K}cÉ‚¦%#\‘R±xH–3Íà{ISà¾¿zM¬þGä'Ah@ €æb"K`âº4~yH˜P%pÁPÈq,–@!5>ƒ gð,„kz|:t
êWJ–EÄFäwD#Ÿ’œÁ¡yÊ¼J€®¤"‡ÿP$8(-¡…o‡ÕNÅQ
Ø÷õå.öýQJM{òµõ/˜C
ŒH Bc€žˆ¼4øD ¿#M¼š²Ì	OÈW˜@ÁÊ­ ¹ ·æ:Q2-A†å/HR0îCƒ6²rZÐŒiVŒÈàˆØÍò”‘D¿pQÞ‘ÈÜ¡†äâ¼`"å×ølˆaìÏ%°ž/6ÚboÄvˆ2› ý û
©h¬BâÃ½qÈ×ã¯¾ƒkœ,ø…àÔ¡²mÁk^5®¤é¥´×†œ7DFÊ[éÛGÔ)³h°.dÚ“+ù[zÞk’¤tJžq¼|NPƒ»D-(<se¿#wÞ•1wcìœ§)J«ä'p=u|¿œ891H™UseåS­Y–k$4R•™õÀ(7¼—O^™¡ÜûPüÎ³ŒÅºº†Ädbc„wï~ÙFZgÜßhÁ¾qp›w@}T¶r}€¡P	<c¾eªœ¨•‚n|ƒ½pZ‚ÑF+À–‘ËQO„¬¦_îà¸1îžÝ]Ù­øŸ¬AíÌ ¯ÚŒúqås.Ü'+È_8•úâ]rÑ°‚ù.ø¬d@;#ªØšóÃð!§
)rÃ m‡ü±ùì`‡ZÒ<ç„õéJ‘ûà{€Ž’Z«nÀD_¶1l‡¸w¼!¯·¡¶èúQõß²c[¶‘T?ÙE_%¤$Â^¶y¡ý{b-B€YÎN—0Iç\Þ,ŸÎzzÄ!ü¢à9ùét1¥&–¶Ú‘ÿ ˜W ¤^2& ò^@#—Á¬¨:kÎ;|C´LÌû²@)M‹óCt|Èm´L&>Å´n	¶¸ÂxÌ©;|39;8’gà'&,A‘@0	Ý!ÍC[¶Ù_}ÕÍFX?0èy‘oÈƒ i:=¿ARÔç?½8&nŠéµœÃÖá£±¹.&\¡ƒ¿±0Îk?ˆ‡u‡	×-È<Â
 g8–ûV´y/!»¨är‘€CÌqæf06£âÉ8iŸ? t!9¤ËT@
 ±^/Ÿ"“ìa:TÈ,xÌÅà=¥`xùQÌÂžŠ—ÔML1¨ðÈ<À6Ì@¾ð$Î—óù!›`àÚKeìXÍ¨ï³š2ªµ_A¹‰”)ÖÿÙi¶:s¬	2l-ù«~Î†¡O¤„G‰5cåRx¥‚IW 9‡mb+€Kö 8üÏ“Ð7Rp.æ
m¢f²Lcã)r37€ÆŽ!á„ùÁ€Å†Þµ¿qýx€.£ÿ?#Á—@ß*rwÀ¬ÖÝº‹äPN5+5R'œÝùI+ï/98átIW
çá%··àAooG6©ƒÈïû~¼þðËïCCåL¦Y?÷LäÛ8
Û»‘]Ì$´—¸ÀBÄ¡(-sçÿ•É ó6
UÓe½-#Ì)¡WQ_Ýª24µˆ²iA.ù¾
š±·¾U·v*\˜"WóåÕ­ëáÈŸ‡„³õØXwËŒZÜvÉ^XG®R?>S7ÄVÍnk­Ø›0Û"¯/Œí°6™£²èùÕtQ˜ZUsK[³ìÄÌ	<N‡üo	&bbrS;I:Ð i€õÞA‚ÂÀ#Ç_°ÎuËmä¾˜å–ù’IYÅp	å©Éá7Å¥ƒØÜŠ	5:‡Vo?(–”©QtMé”Ïwe–“„§ÌVcCÒ&ug¸èIdlÛCQ OVgGÔÇÉÅkëÁ­OÇï!â…¬Â¡ÝÊ4Ú:XS#9ø±¦·"Zr'ÞUchîeÔ¾	}#T˜0›¥¬ãŒ- .€Ä„Å®+0óžFc¦ôBõ*çP’—‹@š)á¬!°l ¬NŒ]	).Ì”oŒîsš´*È$xh¸Ä•9™,…¶iú^4ŽNà}¥ ÈÒ¶,Á±e4"KZj\‘ï!wÑë]´®ÊõùÊ5/lÚ†1yýìpìCÏÎ`¼°Ge/× öÆG›ùå9Ï›q/àVuÒ8¶R„øO¬ü×Q¤¡Zò”cÔ ¡sQF¦
Ý43çÂzÞ¦£Í¨6&ðA4äTQÂœ±çe¾ªÖÇùøŠj›öÖí¶ú!N5{Ô³ê4¬
´¾Éž€û0 |ÆvXßtvð?ÎQØáoî=€SñÉ»÷f3µ:¶‚Áõ×ÝÀÐAIÎ0¸{ï±ôŸjDÚo1…ð‘§Ûñþú77"ùI•ª%&•ÄÕY×izŸ47M€¬´£zsü³Aq¥âªŠn,³ewóŽá‰ˆæ=KÁR[=Àzª!tk­l”†+.ØÝz®ŽNxŒK@ª¥Y¥Æ=6Æ¬Åb¥¶T@e§ªº5'fÂ,•+;O:8”J9èÃ&Åæy»#'vsÎ#ÝAWë7*J‘Ì2tBáG·ëˆØ‹p¹››Þi¸¯mÞjD~(
Y v6¤Æ9¦¢(s¸04ðØ^BÎ¥Ò=zÝÑ£‰?8xkxì@ î¡;€á­GŒa)·ƒÙu1º·ýMî°5°Ømnç¹Úá™š›–ØÇÌüú†]&ÖcµŸyíU…A³’ç—~<*«ª&ö>Û¨dÞÊ¦z ö2©½pòlj‚A+7}jvÚÔìAàB¤-Ž}–vÒ,­Žò´ü=Š§IØÚãg™báIr·–fø9FØ§ÇMãÚ_‹ä 	uÛQ3º–×	¶>¹;rr×ÖÔÚg}žwä<¯µíÂ,ˆ@oœÏï‡=ÇAl9ãš©œFìB1|w.Gó?çÁWIp» pÌ˜±¥QÄ “¢¢ùszJ¦Uºp]2?Ô¿Ëñ–ë“ñß<~ûü[³žùíp4½0ï0ÛU­2ãÚ¼%cPæC¥q¹Z™ü{ešÒÌïtÜk à,Ò~­´Z_šRQH´sÝ[ÏŸ£òçLØÓ‰;&èysTÞ·âÌL	ÄuáÌ½LÙåWØ4ØÊ™÷t92]dþØÙ`ëÀAëJD7þ—çÅ–Ç”>„Z¬¸Ò}QQ-§Í<QÝ²ˆZã{Îµ…ÎÃ}ö·¯É÷œ’õRZš[ÚÞÆ¶f!-ómß(×ã¶±ÕzôŸÏáœ¤¶F×xÃÛ¿uDÚ ü×Ã·¿ýY5Àüà¿Ö×£wãÛ¼5<Zš^™Çö-¾=xû˜^¥Æ}MÏ
jkz=z‡0½½Á[Ã£Ë´lŸ¢Ÿ¨ºÒrŠ¶e’¾6IØ£xšBKÛIÛÎ(ö5—Õ\:˜áÎþÐ{üŽ[~éb€ðë+1'®Ät0ÃV©ü=ð÷Xž¦(ÓÅ$;bÙ×gNZŸéf”;süûñï<z©¦£Ev ²¯Úœ´jÓÁ »eþ}ç¤œ.ÙÈ:¿ì—Rw)U¥èÇ½dóBl[ƒç²£ž<Ç'¨ù³×ââ:R§ËZ¼u2ì"ŽuP=wŽÏ«é3ð<k”ØÅ IÁè¼'Ðñ	d}üib}lÊÒóçøü±š>­Q¢ƒ°ÕSè$BUŸ‡¤ØF¢hÆâò7Ü9VÞl^w;[Á-ªuV@»þ¤n?4«¶\fË¥*^šW¿ŒeôÓ˜p$©¶5õ÷¡@Mïà	+p_4ž?û†||:_ÇãKø÷»\â–LO‡äéø•9úwwT—@ðR<ý´ÕIdôî&f)=Éödùòõë£îm—öåú|ø’bE2.Jmw¾ÀM$p÷Üt5IèDâ•3Í,²X{t{$5·)]£±yÏµ2nô€›-‘÷Õ¶Z¸å8#è¾2»±çÞ
¤šç)Ý›ŽS¶`)¡Ó‚±Œ	÷eO'»ÝJ &Ah€u«`3/|ªßaÓú;è½eÍ`íUÎƒ&©µã©Žá6ªˆÜ“ÿ™®Š   0707010005c859000081b40000000000000000000000015ab2175400000e7e000000660000000000000000000000000000004100000000root/usr/share/doc/opensvc/template.service.sync.evasnap.conf.gz  ‹      í][“Û¶~÷¯ÀØ“‘=ÕÊZ×éÃÆödÛØÓL“83ŽÓÇ³‘ „,	0 (­2ýñ= IPÚ•HëÒU‡z°W”xxp¾ïÜ ˆ|òdŸ¯GOÈ_(N/E4$f™3ÂæTšï$n/·_Û=ú„ƒ}2þüè;Þ×åˆ=BÕoØr!U|Qk _	š1øðl/4“ŽdN')«®öŽ¦/Dû£àŠÕzü¢
ûA®äœk.Ó‹Æ)1Kh‘šZóŸ¤°p1cŠ*"¡”Ñ„¼!3FãRF`ÐL¹ÏuÎ"žð¾3eÎ¬ià
:‚oüÆ 2!fÆÈ?&o½$T)º$3©¨e?Ð²P#TÄ«gÒÀùs1=iß”Vû'RÞg{<µ7þ}Æ¯+'¿³ÈXsRM4c®K´Ö…µueÅÍÆÎ)Wº7´7ô%ù]Kq–H•QcXLR®Z>æ‘Í¨âLÃrÅ4ïÇ-GA(|K£™=•dÈ™Ñ9³ßh†dk3°Þ2È¨¾ <zD~oà{|›Ó´`„kB+­~{…Þ—S3{óÛ«´<~C´Q –ó7«Ðü§WQ^…%Îk¨pÞZC‚s&b]Ž£†]åSô¦O¨óç&EÒT.ÀÝ”ƒ\InšI&€”"]:7uW
¡yGäð/DL Zü×Aªˆ¢bÊÈÏ‡/ðÀøì……Òá`âûÁÌXÖƒy80Á¼R-‰1kxúÁq„¹3¸ˆÛVpWzâ{ˆ7C‡¥˜3(À…aS¦ðÿ‹)ÁÒRÙ:ÂšÅC²˜qß….h
<€Ãçã/‰³ÿˆ|/ ÐœMd•7Åd1$LèB¹_r,–@é®A€3ø)…ÌmtðàA*˜_»ºÊeø”äŽ@JYi +©ÈQ¾(H”– †*õðù!¤°ïëómìû£†öäk_08?Ae€žˆ¼´øD ÿFš”!hªd‘ž¯0‚UXrÁhí9t¢eZ€Ç_¤™A…Ö²rª 64LÈ;Dì–fyÊ†H¢¸(nIdÏÐCrvQ
ð
‘òŒ¼6Ö,!ûs	¬çó5]Ü‰¨‡(²	Ð†ŸñHIÍÀAcš Ç°n»qÈ×ã¯¾…ïxYð…à´¡vºàw^4¾H3é¾rÞ)ï¤oÎ¨WRfW °Q2íÉ¿‘ü-#ï%IR:%OÇ˜ÏŸa4.Ñ
Å\»¿‘;ï[íú{ÃÓ¥Uò¡Z‡šØñârâåÄ ýÙ¨¤µ“%}–$(©‹ÌEà@”Oï6$Ã•—6•—1ÿæYÆbÃN½"1™¸áýûANI±áÿ=¸TN+ó˜É–~ ÕÀØ8Ít1ÑKÃøGá­Ù²`ËÈù¨®'BÖÓÏ·pÜ:wÏî®ìÖüOÖ ö[›ôu›¬¿VW>å"À}²4L?ó$*ÌÙûä¬á ?Yñ)xÈ =#ªÙJðÃò!§)rÃ¢íRþØ¾¶°C/hžsÂzŠt¥È]ð@GImUŸ01–­¥í·àŒ×äå&Ôæ½S?§þBvhÏ¶’ê+ûê'&H„£VN½ÐÿKbÍÃˆ€]ÎÖ0Io¸¼Z0>õôÚK@ø;TÁ7äû÷0Ä”ÚZÚYwD~E0/ H³ÀIÞó±-yÏ ‘ï`VTƒµŸ{|C´lÍ{²@)CÕé!:ÞÛdpK´l'>Å¶n¾¸ÄzÌ›;üåÖB ŽäÄ‰	K0B$PLâ¬ñ„F7¡/»î/ƒ±¬fíd³E¯ùšÜš¡ÓÓKÊÐ¢>ýôùÙ!qƒK¬ôå¼9ZlD.Õ„E:xV`°Ä¹ÄÃ…C‡„WÁÈ ²a03Ìå¥V mD>Hè.*¹\$àÃ0@·\ À½ÃfÂftÎA\£'!Ýõ„Î%‡v™
hÎDË§È$÷°R2.3D1xN¡X^De³‡p¤â9µT³Ú¾®¹ou:vzb?1?d$Þ©›c©œg3êóœ¥ÜbœýsóJŒwþ“³lõÉ¾ü²ãebEmÉêë¬9úDJ¸”XqöX.DiTpé
$°mmpá”=@Å#ÿó$ŒÃœ‹6Ñ3Y¤±¹MÄ ³cI8ae2`±e@Ú_ûqÜCßÑÿŸ‘à¾3ä*î€x­%†u_É[ <œzV¤N¸ºó½Ñe¼Äõ×tA—vX"¼äú"èõõÈ5uPù}÷öÝåÇ~Zj,f2m¬úùk‚ RÇQ¨ïZw1“ /ñ……ˆCQFæ>þ»ecÍÊ·‰*ÅL¡`´E„=%èyeñÅµ.â0¡éy”M9Óäü‰;Þ”Z]»¥0aš\Ü,.®ýGåç0‚pÕ¡Îõ°lÖânH6yá<r­H}ù@N­ˆ›5»®­â¬Ãì&yË‰±-Þ&s»¨zsºÖ)ì\î°´µ›¹ìšÀÃÈ?KH˜ˆ‰íMÝ"éÀ€¤Î÷6 yþ‚w®zn£÷Å.·È·øðLŠª†K(Om¿..•b{*6Ôæ8{ûQ³¤H­¡kJ§üxWd9IxÊÜllHÚ¤®à,K’[Àv_Õ(Æ“åÉõarñÒEpSÃü=Ä‰xaSƒ38è­­Ò~_NLdrˆcÍhDtäNÊP¥y)£ŽMè¥Â„¹.egÔ€ú·¯À®”ä°³S? Ô,sÅ y9¤ÙI#\5–€Õ‰õ+!Å™]ò1|Þ€%	2	¾â§9™,„qmÆ^tŽ7Này… ÈÒ%j–`nÈ‚*Kòô.fuˆîÒµCù1_xõBÕÖœ©´Ï–À^1ôä¦ö üëÀÞÆh»¾|ÃófÝ¸Uƒ´­!å«*Vþí)Ò0-yŠìà²";Ã´+çÂEÞf Í¨±.ðQ4äTUÂc9®Ê|YíŸ,ë+j\Û[ëíô}œjŽ¨gÕqXX}=va@xÍp±éäà˜YØãoÏÝCP)›w_îÍ(vj
¶„dI=Æ}7:(ÉwJ,ËW•‘†®Æ›O¡|ä©A=>\þä3R¹¨Rib[IÜu™¦wIóËÈJ—Õ›ùÏ	ÃI”Š»"l)º¶ÌM#ø“·¤?&"š÷,=KÝìÎéB¿×ÊUi¸ã‚Ý®öê¸á„Ç¸¤ÚšeQjœãjÌZ,ÎÔ¨,â´AU¿çÄ®@X‚¥réÖ	Ã ‡R){ø__àõ¶WNìê”3Ýá~É,Ã „~¤¸ÛGÄnY„ÛÝüòN#|mŠV#òV)© ;WRã“REn‚æ«“[6óKmzôº£G·<¾wðVðØ‚@ÝC·Ç[­?Éiïv]œ®Çmw—Ûl,¶»Ûiîvx@®æ—%vq³rÃ6ë±ÚÍ½v…ªÂ [ÉÓk?”WU{_ìT2oåS=P;¹ÔN8•lj‚Á*W}kvÜÖì^àîC¤-Ž}—vÔ.­Žú´ü=ŠÇiØÚ9ã¹báQz·–nø%NØ·‡mãÚ9_‹æ 	uÛA;º–×	¶¾¹;ps×ÖÕÚw}Ÿwà>¯µŸmÃ,¨@¯¼Oï‡=‡Al1ã†éœFìL3¼wExs.¼•w{  ÇŒ¹[E0Q…ÍŸÓS2,=¨Ú…ËZý¡þ-ÞåK¯.Æ¿R<~óô•ÝÏüf8žÙ{Ø÷nW«Ì¸±wÉùPÜ®Väƒò¾2Miîîaî6 pA™r¯´^ÝšRQH´uß[ÏŸƒòçDØÓ‰;¶èysPÞX·âÌ=L	ÄuáÌLY“Uî°h°‘/2ïér`ºÈü¡³% ÁÆÄEëRDWv¿ÓÉ±å!µ¡+®tßTTËi³NÔC·‡.¢¶øŽkFm¡+aŽUßýíêr`Ã—dK)-Ý­mggÛ³‰–Ž†¿ùvw”ëqÛÅÙj;–¯/Íp^R[§ëÜ‹ãíŽß*"íþëáÛÝÿœ÷à~ð_ïëÑÛ‡óíÞ
-]¯Ècwß¼]\¯2ã®®çµu½½}¸ÞÎà­àÑeY¶oÑ4»Òr‰¶e“¾²HØ£xœ‰–¶‹¶Qìç\Ž4çÒÁ·6ðw€ÞãwØé—.Ø¿~&æÈ31Ü°U+ü=–Ç™”éâ’±ìçgŽ:?ÓÍ)·öøwãßyð©šŽÙÈ~Öæ¨³6²[çßOàu§‹C¶²î/û­T‡ÝJUúaoÙ¼ƒ›öàùî¨'ÏáÉfþâ½x¸ŽÔé²o•ÛˆãTÏÃsÇYú"Ï
%¶1h¢½é	txYCŸ š„ØF×²ôü9<œ¥O€@+”hÁ œ†ê)t
¡©OƒCRl"Q4cqq‚Ü9Tßlow;[½Á-šu¦@¯?©š3Û¾vË…VÏí-ŠŸÇ2zŽmÌ8’T5-ÏC†ÞÂ¦ð¹h¼òÓ×äÓãñøb<>ŸÃ¿ßþíÉôxH_Ø£õGM/ÄãÏƒDFo¯b–Ò£<>£‚l/0Ÿ¿|yÐgÆ…»¹~ƒÞ¤X“Œ‹Â¸'_àC$ðé?øÐi´$¡‰¨œñhæÅ¹GÿŒ¤æcJWïhlïs]Þô€["ªÇjá#/ ÁXð–Ùgî-A
¡yžòÈóÐßé8es–:UŒeLøû(—tr[	Ä$°n<Ì¯Z>aÓÅ;½cÍ`åVÎƒ&©uà©ŽácT¹Gÿ›·´Ž    0707010005c85a000081b40000000000000000000000015ab2175400000e86000000660000000000000000000000000000004000000000root/usr/share/doc/opensvc/template.service.sync.hp3par.conf.gz   ‹      í][Û6~Ï¯ N°Ç“¦û0íÝ6ØbÛ¦Ø4»A0CK”MŒDª$5ýñ{II”=¶¥øÒq!?$]Éó}çFÒÒ³gûü<yFöøAqz!¢!1‹œ‘YþeNÕnâöøqûÕÝ“8ØgãOO¾¶ã½ô#~ò{~Ës©â‹ºT)º€3g{ü Št$s:IYÕÔ¯ª`xB±ß
®X¼r"WòŽk.S{ò-Mµ=³„©©{ý³ö3¦¸¡"òí¤Œ&ä™1—`Ì”;¯sñ„GpÍ”	¸7²jtWüL3¸<!fÆÈ¿~!–)VAÄH¢™ˆI$³ŒŠXÃÜúµ;{I)×iXEÓ^¿ëõ«X&Åæ2U²ÈGäW8¢¡£hº†Þ“¬Ð†LX`|…f1Â’iº°(>aV´byÊ#j`¬pE.S9]X´ ŠÍXe2fÇ «Rý£C‹øÍcj˜öMPô(äëEpþ'Ð3jî‚¼‡KfJ
Yhhˆ\éúoÔ?^ Xk!`f&ã?Õb*Uw AëÙ!1 ñ€@”ò h(N_h	`ÖO“Œ›ÐSY£°žJ[3ð·\ÚN¯!Ÿ^G9¢vb8l4†Ä,¯kÁ‘b€Çu>Ñä#öùS‹«4•sêŽG{1m•?á7¤ '…0øÖÀw¡zGäŸð/`z¶úªIÊÁñ))*¦Œ\ñð|ø
ŒÏ^!–' ¬3cYæáÀõJµ ÚXÕÐOÁq„¹3¸ˆÛVp×zâ{ˆ7C‡¥¸c*è †M™jÀÿo¦KËÎ’;šŒpmsŽ!™Ïx4ƒïÅÌŸ_½&Nÿ#òƒ 4  @s6‘°FsSØX9$LèB¹\¥äX, BëÅ3x–
Â=¶	Hõk-1—0ÑÄ§$gpDž²R%@WR‘£üP$¸K $tC•ýp¢A)`ßWçÛØ÷[!íÉ×Ö¿ `)0~ ‚
z*ÄÈ¬X½Kd3dÂòE &P°r+@®T;jÑ‰–i2m	c°C+Y’C’n˜‘·àˆØ=Íò”‘D?rQÜ“ÈÞ¡‡äì¼`"åq`Ûp4ØŸK`=¿[é‹»û!Šlôƒág<RR30P—´”*Ä1
†zã2‘¯Æ_|×xYð
Á¨Cíú‚×¼j\H3sé®9o‰Œ”wÒ7GÔk)³kè°Q2íÉ¿‘ü-=ïIR:%ÏÇÏ_ 4à.Q¶¢ˆ¹vß‘;ï
ƒ˜û{ËÓ¥Uò¸žz¾‚_N¼œ¤¿Cõ­vò©1,Ë’:©‹Ìyà@”ïÖ%CËÊKŠßy–±˜Ã°Sß‘˜L\ŽðîÝO ×Iç±áŒÿ-¸ìÜdQÅP•-ü 4 ª!múo{I¿^hÆ×8
¯%ˆ­ [FÎGu>²˜~¾…ãÖ¸{vwe·æ¿³µ¿·A_·‰ú+yås.Ü'(_xæì]rÖ°è?Yñ)XÈ€~FT³%ç‡éCN5R*ä†EÛ…ü±ýla‡žÓ<ç„õéJ‘‡à{€Ž’Z«>`¢/[	Û!nÁ—äõ&Ôîz£~Fý7„ìÐ–íæR«–}öå&SD8jåºÚI¬»Ð#`•³Õ%LÒ[.¯çŒOg=½öâþYð-ùá1¥6—vÚ‘ÿ"˜ ¤™3& ›òž@#_Á¬¨kÏ{|C´lÎ»vƒiCÕé!:Þçœl´l%>Å²n¶h—Ž¼úÀ±Ã7[³È#y~bÂô	$“¸Ü1¡ÑmhË®úË`¬³ÙçÜR†yIÖ‚fèôô‚2”¨Ï?~zqHÜ ‰¥šÃÍÃ%Vc#r¥&Ü(ŠÐÁß8Î²\orîÐ!á»`d Y‰°˜Æò²W mDÞK¨.*¹\$`Ã0@s¬À¼Ã
fÂfôŽƒ¸F1NBºö„ÞIå2Pœ/ˆÀ—O‘IîŠ°R2š¢¼§ÐL/¢2ŠÙC8Rñ’ZªÙÞ^VÈ¬aV ìô<Ä~|~È&¼Sá×!½±ãlF}ŸÓ”U­ûºmMÒÿÉi¶:s¨51ì-ù£ngÅÐ'RBSbÉØc9¥RÁ¤+¼Ã¶¹À…Sö 8üÏ“Ð7Jp.n5ÚDÏd‘ÆÖSä67€ÆŽ)á„•Á€Ån-Î»öK?Ž5$ðý_ŒúN‘Ë¸.`µ–èÖ}&oòpêYa:áêÎF—þ’ƒNçt¡q÷DxÉÍxÐ››‘+ê óûîû·W~üuh©1ŸÉ´±êçÛAeGaWª‹™„þŸXˆ8edîý¿¶€fåŸ‰*ÅL¡`´E„5%ôó"Êâ‹]Äa@ÓwQ6UäL“oà« {SöêÆ-…Óäâv~qãG8*ÏÃÂU‡:6ÖÃ²Q‹»!Ùà…óÈuGêæ9uGÜ¬ÙM­w`f7É[NŒm±6™£²èéÍéZ£°sTß:ÚÚ­ovMàq:ä_$LÄÄÖ¦n‘t`@Ò ç{	
 <Á:—-·Qûb•[ä[lxH&E•Ã%”§¶†_—J±½jtw8{ûA³¤H­¢kJ§üxWd9IxÊÜllHÚ¤Îà,K’[À¶.ëãÉŸ»ï/”8î|j¿‡8/lhp
‡~kÛiç`ILDrðcMoDtäNJWm÷´yµoGßH&ÌU)Ë8c¨O ±`qû
ìº@IŽrçÜ£Yä<ŠAñrH³“F¸j, «kWBŠ3»ä£û¼M:ØýypÉê¼LÂ¸²}/G‚'ð¾B dé{–`lÈœ*KòÔ.fyˆ®éÚ ü˜/|÷Â®­S©Ÿ-Ž½bèÉL)ìQÙæ5¸“¦~ëÙ-Ï›y/àVÒ:¶B„”Ÿ*YùŸ§HC5´ä)Ç¬RgUDv†iWÎ…ó¼MG›QcMàƒhÈ©²„[Ær\ÄM§~?j•_QãÊÞºßÎA¯ãTsD=«ŽÃª@ë«ì	È°Â66sÀù¦“ƒÿqFa¿½wN¥,Þ}º7£X©)Ø‚õ÷Ý@è $g˜Ü½/±,?UDºïn
é#OöãýÕÏ>"•‹*UOl)‰»³®Òô!i~™ Yé¢z3þ9 8‰RqW„MEW¶ƒ¹ió–ðÇDDóž¥‡`©›=Àù ]Cè÷Z¹,w\°ûåZ7œð·€T[³,J{\ŽY‹Å™ÚB•Eœ6¨ê÷œØK°T.Ü:aèàP*å WÛö¶gNìú”#Ý¡~ÒrUn´Çjƒâö±{áv7¿¼Óp_›¼Õˆ|¯”T€K©qI©"7óÙIÍSÈ¹Ô¦G¯;z4qËã{o	-èÔ=t{0¼åŒ1œÊí`v]Œ®Çmw“Ûl,¶›ÛiîvxD¦æ—%v1³rÃ6ë±ÚÍ¼v…ªÂ YÉÓ+?•UU{ŸmT2oeS=P;™ÔN8•lj‚A+×}ivÜÒl-pëi‹c_¥µJkc§:­âq
¶vÆøY¦ØCx”Ú­¥~Žö¥ÁaË¸vÆ×¢8hBÝÃvÐŠ®¥Áu‚­/î\Üµ5µöUC_ç¸ÎkmgÛ02Ðk¯ÁÓûaÏa›Ï¸a:§;ÓŸ]ÛÑÊŸóà£$¸Û 8fÌ­ØÒ(b€‰*„hþœž’A éAU.\Õ‚ìõïsü…åòbü7ŠÇožc÷3¿ŽF£ö™öo·«UfÜØ§dŠ|¨nW+òAù\™¦4û;ÿ hE¦Ü+­—·¦T” mÝ÷Öóç ü9ötâŽÍzÞ”7VÇ­8³†)¸.œy)+²Ê¶6òEæ=]L™?v¶$Ø˜ i]ˆèÚîw:9¶<¦ò!ÔbÅ•î›Šj9mÖ‰zèöPEÔßqÍ¨-t%Ì±ê«¿]Mt¸ã’l)¥¥¹õ íll»a"ÑÒÐð7ßî‰r=n»[­Çòó¹ÎKjkt=€{1¼Ýñ[F¤½Ú§×÷ðíjN{0?|©@{ëëÑÛ‡ñíÞ-M¯Èc÷ß¼]L¯Rã®¦çµ5½½}˜ÞÎà-áÑeY¶/Ñ4»Òr‰¶e‘¾´HØ£xœ‰–¶‹¶Qìç\Ž4çÒÁ·ð€ÞãwØé—.Ø¿~&æÈ31Ì°U)ÿ ü=–Ç™”éb’±ìçgŽ:?ÓÍ(·Öøãßyð©šŽÙÈ~Öæ¨³6²[åßOàu§‹A¶²®/û­T‡ÝJU)úqoÙ|€›öàùê¨'ÏáÉjþì½x¸ŽÔé²o™ÛˆãTÏÃsÇiú<Ï%¶1h¢½í	txYEŸ š„ØFW²ôü9<œ¦O€@K”hÁ œ†ê)t
¡ªOƒCRl"Q4cqq‚/Ü9TÝlw;[~À-ªu¦ _¿Sÿ>4§¶\VË…V/í#Š_Æ2z‰eÌ8’T¯5-ïC†ÞC	Sø^4^yö’||:_ŒÇgãsø÷Û¿Ÿã+™žÉÓñ+{ôKÔ@ðB<ý´ÑIdôþ:f)=Êë3*Èöóùë×}·a\¸‡ë78ðAàCŠ5É¸(Œ{ó¾Dßþƒ/FM:‘øŠÊfYœ{ôïHj¾¦tù‰Æö9×åãñEø²%ò¾z­¾òœŒ™ÝxçÞ¤šç)<ý“ŽSvÇRB§Š±Œ	ÿå’Nîu+˜¡Ö-‚—ya«å6¿ƒÑ;Ö–å<h’jP;žê¾F‘{òTêÃ02    0707010005c85b000081b40000000000000000000000015ab2175400000e0c000000660000000000000000000000000000004400000000root/usr/share/doc/opensvc/template.service.sync.hp3parsnap.conf.gz   ‹      í]mÛ6þž_A$(œà¼Ž7MïÃ¶tïÚàŠk›iz‚`—–(›°Dª"e¯‹ûñ7Ã‰òîÚRüÒuOþxõ2"çyf83¤ÅgÏöùyòŒìñƒâÔJDC¢W9#³üËœJÐüóÅí·uûÕÝ“ØÙgãOO¾6ý½züä	¶~ÎVKYÄu#hQÐœ9ÛãÕ¤"™ÓIÊªGýZ”Oì÷’,¾s"/ä‚+.Ssò-M•9³„–©®[ý³æ3VpMEäž“2š7dÆhìÁt˜ö¼ÊYÄÁ5S&àÞÈ¨ž "¸âgšÁå	Ñ3FþõAÝY-‰b"&‘Ì2*bFpë×öì%I¤|HÃ‹Åµ ¹êÿZÉ4'¬ä§\é†ö­M!ÈY5“šüöÈåksþ(Ê<¦š ¼v7cO¯£¼üs!¨4½/:(?f9°WÕ‚£‚¯ó)ˆ&±ÍŸX\¥©\‚Ö‹¶8b
IO&¬@ŠteŒÄ=a€zGäŸð/Xe}	Us„³ bÊÈÏ‡¯ðÀøì¢ép0ñÀÃ`f,ëÁ<˜ ^Y¬ˆ ``ùSpaî.â¶\`ÀµšøâÍßãT¹ÐlÊŠüÿf…`©o,YÐ´d„+R*ÉrÆ£|/i
<€ÃçãW¯‰Õÿˆü  ÐœMd	¬Q\—TƒR†„	U…Çb	à½á8ƒg© \«Ð3à3Ð©ƒTP¿R²,"62ã @|JrG„æ)ó*º’ŠþC‘à@B ´„f¾jT;G)`ßWçÛØ÷{)5íÉ×Ö¿ `)0~ ‚
z"òÒàüŽ4ñ.hZÈ2'<!_`+·ä‚Þš{èDÉ´–¿& ÓØ ;YÀAp Y1"oÁ±[šå)"‰~ä¢¼%‘¹CÉÙ9x=*À*DÊ3®ñÙè5ØŸK`=_Üi‹½Û!Êlôƒîg<*¤b` &X¬TˆcôÆe<"_¿ø®q²à/‚P‡Ê¶¯yÕ¸"¦—Ò^rÞ)o¥oQ¯¥Ì®¡ÁºiOþäoéy¯H’Ò)y>ÆQðü:Aîµ ðPÌ•ýŽÜyWjÄÜ±sž¦(­’ŸÀõÔñürâäÄ ýä·TÍ••OµfY®‘tÐHUfÖ¢Üðn\2<ye†rïCñ;Ï2sèvê“‰Þ½û)di=6œq£ûÆÁm~Üõ1PÙÊõ†@5$ðŒù–©r¢V
ºñ5öÂi	F­ [FÎGu<²˜~¾…ãÆ¸{vwe·â°µ¿7ƒ¾j3êß‰+Ÿsà>Yi¦^8•úì]rÖ°h?¹àS°.íŒ¨bkÎÃ‡œ*¤TÈƒ¶òÇæ³…jIóœÖS¤+Eîs€ï:Jj­º}Ùa;Ä-¸ã’¼Þ„Ú¢7êGaÔCÈmÙFRýd}•vN{]Øæ…öï‰µ=f9[]Â$sy½d|:ëéµ‡ðˆ‚çä‡wÐÅ”šXÚjwD~C0/ H½dL >&ä=€F.ƒXQuÖœwø†h™˜÷!dRš§‡èøåØûÐ2™øÓº%,Ø¢)„;õc‡o&g'Gr,ÂNX‚"`ºC&4š‡¶l³¿úª1š°~`Ðó"/Éƒ i:=½ARÔç?½8$nðˆµœÃÖá£±¹*&\¡ƒ¿ÁÃ
t–XûA<¬;´H¸&h@æVÀ =Ã±Ü·
¤È{	ÙE%—‹l:h`Ž0ï0ƒ™°]p×HÆÉ@Hûü¡É!]¦R õ‚|ù™d¯Ó¡BfÁc†(ï)Õ Ã‹ÈbæöT¼¤†j¦µ—2°3vzb?>?d¼S[c©Œ«õ}VSFµöëæ™gü'§ÙêÌ¾ìF7Ž³X~`ÅÖ’ÿÖÏ¹cè)áQbÍØc¹^©`ÒHÎa›Ø
àÂ’=@Å#ÿó$ôÃœ‹¹‚D›¨™,ÓØxŠÜÄ ±cH8a~0`±a€wí—®Àeô1üè[E®ã¸€ÕB [w‘¼ÊÁ©f¥Fê„³;?håý%'œ.éJà\"¼äæ<èÍÍÈ&uù}÷ýÛ«?þ:4ÔXÎdÚ˜õsÏA¾£°½w²‹™8Ík‡¢´ÌÿW&PÌÿÙ(TL—ô¶Œ0§„v^DY|q£Ê8ÐÔ"Ê¦9SäøŠSÇo|«nìT˜™j¾˜//n\Gþ<ô œu¨ÇÆº[fÔâ¶KfðÂ:rÝúñœº!¶jvSkÅ¸³-òúÂØk“9*‹ž^M×…©P5·´5‹yÌœÀãtÈ¿H0“›ÚIÒI¬÷6 9þ‚u®[n#÷Å,·Ì·ØðLÊ*†K(OMW\*a0ˆÍ­˜P£sX`õöƒbI™E×”NùxWf9IxÊl56$mRGp†‹žd@Æ°=uŠñäÏ]Žô
¬·>5¿‡Xˆfh°
‡v+Óhë`ILŒäàÇšÞˆhÉxW¡¹—Qû&pôPaÂl–²Ž3¶€º »®ÀÌxr™ÒÕ«œG@1H^Îi¦h„³†À²°:1v%¤83S¾1ºÏ9hÒª “à¡áW6
äd²Ú¦Eè{Ñ8\8÷• KWØ²Ç–Ñˆ,ia¨qA¾ƒÜE¯wÑ>º6(×ç×¼°iwŒÉëg‹c¯zrã…=*{Á¸°7>ÚÌ/ÏyÞŒ{·ª“Æ±•"ÄÀª`å?Ž"Õ@Ð’§£‹22U0è¦™9Öó6mFµ1¢!§ŠæŒå87(ó•_½XÅWTÛ´·n·uÐqªÙ£žUÇaU õ»ì	È°Âglæ€õM'ÿã…þæÞ=8Ÿ¼»poF1S+ c+Ì`PqÝ”äƒ»÷Kÿ©F¤¡ñSyª±ï¯~v#’ŸT©ZbRI\u•¦÷IsÓÈJ;ª7Ç?+'Q*®Š0¡èå`¶ŒànÞ2ü1Ñ¼gé!Xj«XR5„n­•ÒpÅ»]ÏÕqÁ	q	Hµ4Ë Ô¸ÇÆ˜µX¬Ô–
¨,â´AU·æÄÌ@‚¥reç	C‡R)}Ø¤Ø<o{äÄ®Oy¤;Ô ®üÌšû¨àv»e.wsÓ;÷µÉ[È÷E!ÀÎ†Ô8ÇTe®æ¢“[óK¥{ôº£G;=¾wðÖðØ‚@ÝC·Ã[ÃRn³ëbt=n»›Ü>`k`±ÝÜNsµÃ#257-±‹™ùõÛL¬Çj7óÚª
ƒ6f%O/ýxTVUMì}¶QÉ¼•Mõ@ídR;áäØÔ‚V®ûÔì¸©ÙƒÀ=„H[û,í¨YZ;åiø{“°µ3ÆÏ2ÅÂ£än-ÍðsŒ°O›Æµ3¾ÉAê¶ƒft-®l}rwàä®­©µÏú<ïÀy^k;Û†Y^;žÞ{ƒØrÆ5S9Ø™bøî
\ŽæÎƒ¯’àv à˜1;cK£ˆ&E)Dóçô”Mªtáªd~¨›ã/,×'ã¿)xüæù7f=ó›áh4zaÞ9`þ¶«ZeÆµyKÆ Ì‡Jãrµ2ø÷Ê4¥™ßé¸× ÀY¤ýZiµ¾4¥¢hëº·ž?åÏ‰°§wLÐóæ ¼1:nÅ™˜ˆëÂ™{™rG–_aÐ`#_dÞÓåÀt‘ùcgK@‚­+]›õN'Ç–Ç”>„Z¬¸Ò}QQ-§Í<QÝ²ˆZã;Îµ…ÎÃ}ö·«Éwœ’õRZš[ÚÎÆ¶f!-ómß(×ã¶‹±ÕzôŸÏáœ¤¶F×¸ÃÛ¿uDÚ ü×Ã·»ýY5îÁüà¿Ö×£·ãÛ¼5<ZšžÝz oGÓ«Ô¸«éYAmM¯Go¦·3xkxt™–íSô#UWZNÑ¶LÒ×&	{Shi;iÛÅ¾ær¤šK3ÜšÀßzßaË/]°~}%æÈ•˜fØ*•¿þËãeº˜dG,ûúÌQë3ÝŒrkŽ?þ=/Õt´È@öU›£Vm:d·Ì¿/àµ€ÓÅ Û Yç—ýRªÃ.¥ªý¸—lÞCˆMkð\vÔ“çðä5öZ¼@\GêtY‹·N†mÄ±ªçÎá¹c5}žgÛ4)÷:<Œ¢O€?MBl£MYzþž?VÓ'@ 5J´`–¡z
…B¨êÓàPƒ›HÍX\žà†;‡Ê›Íëngë/¸EµÎ
h×Ôí‡fÕ6‚ëÃl¹TÅKóŠâ—±Œ^b3Ž$Õ¶¦þ>¨é-< aî‹fÁóg/ÉÇ§ãñÅx|6>‡¿ýû9nÉôtHžŽ_™£_º£º‚—âé§N"£·×1KéQ¶Ï¨ ÛÌç¯_toÃ¸´/×opàƒÀ—+’qQj»ón"»ÿà¦Ó¨IB'·¨œñhf‘ÅÚ£Û#©¹MéúÍ{®ýëq£Ül‰¼¯¶ÕÂ-/ÀA_ð•Ù=÷V …Ð<OyäxèÞtœ²K	ŒeL¸÷({:ÙíV1	B¬[›yáSý›ÖßAï-kk¯r4I5¨Ou·QEäžü¼>’9Œ  0707010005c85c000081b40000000000000000000000015ab2175400000e8d000000660000000000000000000000000000004300000000root/usr/share/doc/opensvc/template.service.sync.ibmdssnap.conf.gz    ‹      í]mÛ6þž_A$(œà¼Žw›ö€m7è¶×ÁµÍI®‚`—–(›XITIÊ».îÇß_$Ê»¶¥ø¥ëVþxõ2"çyf83¤ÅgÏvùyòŒìðƒâÔ"†D/
Fø$‹•Êi±…¸Ý¶n·º{ò;ûlüéÉ7¦¿uŸ<ÁÆß°Å­ñyÝ†‚r©àÌÉ?¨%‰‚NRV=ê½,žì÷’Kß;QH1çŠ‹œçSsò'š*s&f	-S]·úW‘›<Ÿ1É5Í#÷œ”Ñ„¼&3FcßF ÃLÚóª`Ox×LY÷FxY$ò9“ü”+môVÿmÆ5SØ‰b•T³Ø\ED×ÌyÄ¬&GpÛ7æ(<þú«óñøŸ_â·¯ñÛ«U0P)éâoC í÷3Frš1T®†ïF9d&ÒÚdŽ(QJP¹Õ¼"4ñ0—fê.1¸X9äÍ÷¿ŒÎÎ^}yrzvùýãñ*P&Shøß0P.ðê¼nù_Ý€{Ö3•7 }“sÍA¡dB£›©%`†êõ {H©†KRÝ-Ì‰U’²9h]ž×Âø¥­ÀT²€{Xw ë{	`‘§Íh>¯X¨ ­JC³b„Æ[ï-˜²<Äïl€Þ°Þ
¹Ó«¨(ÿÜ1­Òâ® î€lÌ
–ÇªI8_SM>b›?5@¼LSqK“v“4®ˆ	5àµOTP½#òü®9D°¾„*7<J"‘äœŒ‡§Ã3<0>93£¤Å	ÇI8°ÌŒe=˜ûÔ+ä‚ä"6vV=è—à8ÂÜ\Äm#¸À€+5ñ=Äë!~À!ó\³)“øÿÍdÎRßX2§)xuc¥bñ/fð½¤)ð ŸŽÏ^«ÿŒÑ„ hN&f˜V\—TƒR†„åª.
9ŽÅ’Ï À<KsÂµ
=>ÃŒE‰c¶uþ#êÑÄ§¤`p$×<e^%@WR‘Ã(H”ÐéÛáBïRÀ¾¯N7±ï÷RhÚ“¯­AÀR`ü@
ôDä…Á'ÂHÏäÎaÈWž/0‚•[rAomÀ?Q"-A†å/HRLcƒî	daZ–1ÍäˆüŽˆÝÑ¬HÙIô3ÏË;™;Ôœœ‚×£9XEžò2;x6¦!û¬çó{m±7b;ò2› ý û¤Pž gâÃ½qÈWã/¾ƒkœ,ø…àÔ¡²mÁkÎWÒô­°×†œ7DFÊ[éëGÔ+!²+h°–"íÉ¿–ü-=ï%IR:%ÏÇ8
ž¾@'¨Á]¢Š¹²ß‘;oKS9pcìOS”VÉOàzêø
~9qrbþbH4U7ÊÊ§Z³¬ÐH:h¤*3ëQnx7.ž¼0C¹÷¡øg‹15K]Cb2±1ÂÛ·¿‚l#­Ç†3îo´`ß8¸Í; >*[¸>ÀÐ`3®ŠÀ3æ[¦Ê‰Z@š}ƒ½pZ‚ÑF+À–‘ÓQO„¬¦Ÿnà¸1îžÝ]Ù­ø¬AíÍ ¯ÚŒú÷âÊç<pŸ, á|áHTê“·ÉIÃ ý@fÉ§`] ÚQÅ–œ†UH©m;äÍg;Ô--
žƒ°ž"])ò|ÐQRkÕ˜èËîÛ!nÁäÕ:Ôæ½Q?
£þB¶oË6’ê'»è«„´sÂ€DØki›Ú¿'Ö<ô˜ålt	“ô†‹«[Æ§³ž^;qß›*î›·ÐÅ”šXÚjwDþ‹`žú–±ð1!ï) 4rl£Üë:kÎ;|C´LÌ»ºÞ«4•Ç‡èx—µÞ6h™L|ŠiÝ-,Øâã1§>pìðÍVè!Žäø‰	KÐC$LbQËú¡-Ûì/ƒ¾jŒf#¬¸:®yAV‚¦éôøeHQŸüôbŸ¸Á#–r?‰‘K9áZR„þÆ?:K¬ý ÖZ$\´ ó+`€žáXî[ÒFä€ì¢’Ëól:h`Ž%˜w˜ÁLØŒÎ9ˆk$ãdûü¡sÁcœºƒ@c½ÀL  “ìa:$E<fˆbðžR0¼ˆü(faOó—ÔPÍ´ö¢BfÛ0aÇç!vãóC6ÁÀ;µ5–ÊØ±šQßg5eTk¿‚r!V)ÖÿÑi¶:ó¨æÚ.!<»Í½RÁ¤+œÃ6±À…%{€ŠGþçIè‡)8ÏqŠN5eOQø©ôZ8;†„æx×~áú±‚.£ÿ‹‘àÏ@ß*rwÀ¬ÖÝº‹äPN5+5R'œÝy£•÷—œpzK
á%××àA¯¯G6©ƒÈï_?þtùáç÷Ãzú6åŸ	‚|Ga{ïe3í%.°ÈÃ©}¥Eáü¿2€bþÏF¡J2]JèmaN	í<²øüZ•q8 ©y”M%9Qä[øŠ+J^ûV]Û©0paŠœßÜž_»ŽüyèA8ëPu·Ì¨Åm—Üì÷0hHýø@NÝ[5»®µbÜ‡Ùy}alƒµ‰•E¯¦kŒÂÔ
¨º±´5ëöÌœÀãtÈÿ0`"&&7µ“¤’Xï$(l <rüµ‹U–ÛÈ}1Ë5KZÖÙðLÊ*†K(OM_\*¦¸oÅ„Ã«·KÊÔ(º¦tÊo€weV„§ÌVcCÒ&ug¸èIæbl€mUÔ(Æ“?wuÕ_(8°ÜúÔpüb!>7CƒU¸Y”ƒ¶–Ä”ÁH~¬éí€ˆ–Ü‰wÕš{µoGß&Ìf)Ë8c¨ 1a±ë
Ì¼€'‡Ñ˜)ý€P½(xƒäå$fŠF8k, «cW¹ÈOÌ”oŒîó4iU	ðÐp‰+r2QæÚ¦Eè{Ñ8\8÷•9@–šÕJ	Ž-£¹¥ÒPãœür½ÜEûèÚ \ŸÏ]óÂ¦Ý3&¯ŸŽ½bèÑŒö¨ìãÀÞøh3¿|Ã‹fÜ¸U4Ž­ÌCü§
V~si¨‚–"å5@è,ËÈTÁ ›fæ<·ž·éh3ª	|Èrª(á†±çÃ•“>¾¢Ú¦½u»×¯”kö¨gÕaXhý>{2lÃ€ðë9`}ÓÑÁÿ8Ga‡¿¹wNÅ'ï.Ü›QÌÔ$tlƒê1®»¡ƒ’‚ap÷Îcé?Õˆ4´1Þ|
á#O5¶ãÝå¯nDò“*UKL*‰«³.Óô!inš YiGõæøg‚âJÅU&½·Ì–ÜÍ†?–G´èYº–ÚêÖƒT¡[ke£4\qÁî–su\pÂc\R-Í2(5î±1f-+µ¥*çqÚ ª[sbf ÁR±°ó„¡ƒC©”ƒ>lRlž·9rbWÇ<ÒíëW6— Í,C'„~$¹]GÄîX„ËÝÜôNÃ}­óV#ò£”Bv6¤Æ9&)ËB.ÌE'<Ö—¡t^wôhb§ÇwÞÐ¨{èv`xËcXÊí`v]Œ®Çm{“Ûl,6›Ûq®vxD¦æ¦%¶13¿¾a“‰õXmg^ÛBUaÐÆ¬Äñ¥Êªª‰½Ï6*Q´²©¨­Lj+œ<ë2¿­\õ©ÙaS³•À­B¤-Ž}–vÐ,­Žò´ü=Š‡IØÚãg™báAr·–fø9FØ§ûMãÚ_‹ä 	uÛ^3º–×	¶>¹Ûsr×ÖÔÚg}ž·ç<¯µmÂ,ˆ@¯œï‡=ûAìvÝ+ñUÜ® 3fgli1ÀD–yÞü9=%ƒ@Óƒ*]¸¬™êßøËåÉøo%_?ÿÖ¬g~=F/Ì;ÌßvU«È¸6oÉ”ÅPi\®Vÿ^™¦4ó;÷ x ‹´_+­–—¦T” m\÷Öóg¯ü9ötâŽ‰zÞì•7FÇ­8³‚)¸.œy)÷dù¶ÖòE=]öLQ<v¶$X;0AÐºÈ£+³ÞéèØò˜Ò‡P‹Wº/*ªå´™'ê¡ÛAQk|Ë9£¶Ðy˜cÙgÛšèpË)Y/¥¥¹õ mmlÛa"ÑÒÐð7ßör=nÛ[­GÿùÜÎIjkt=€;1¼íñ[F¤½Â=|ÛÛŸUãÌþë`}=z»0¾íÀ[Â£¥é•ElßâÛƒ·éUjÜÖô¬ ¶¦×£·ÓÛ¼%<ºLËö)úª+-§h[&éK“„=Š‡)´´´íŒb_s9PÍ¥ƒnLà ½Ço¿å—.Ø¿¾sàJL3l•Ê? åaŠ2]L²#–}}æ õ™nF¹1ÇÿÈ½—j:Zd ûªÍA«6²[æßpZÀébm€¬óË~)Õ~—RUŠ~ÜK6 Äº5x.;êÉ³ò€š?{-^ ®#uº¬Å[&Ã&âXÕsgÿÜ±š>Ï³D‰MšHFozíŸ@FÑGÀŸ&!6ÑÇ¦,=öÏ«é# Ð%Z0ËP=…B!Tõqp¨AŠu$Šf,.pÃ}åÍæu·³åÜ¢ZgÚõuû¡Yµàú0[.•|i^Qü2ÑKLcFÀ‘¤ÚÖÔß‡5½ƒ$Lâ¾h<ö‚||:ŸÇ'ãSø÷»¯OqK¦§Còt|fŽ~éŽê^æO?­u½»ŠYJ²}FÙN`>}õj¯{Æ¥}¹~ƒr|I±"ÏKmw¾ÀM$p÷Üt5IèDà•3Í,²X{t{$5·)]~£±yÏµ2nô€›-‘wÕ¶Z¸å8#è¾2»±çÞ¤Z)Ý›ŽS6g)¡SÉXÆr÷eO'»ÝJ &Ah€u‹`3/|ªßaÓú;è½eÍ`éUÎƒ&©µã©Žá6ªˆÜ“ÿª‹4aï     0707010005c85d000081b40000000000000000000000015ab2175400000db1000000660000000000000000000000000000004400000000root/usr/share/doc/opensvc/template.service.sync.necismsnap.conf.gz   ‹      í][Ûº~Ï¯ 8A½Ž7ÍéÃæ‚³íI€à\R Iû»´DÙ„%RG¤ìõA|gx‘(ï®-Å—®ù!ñÊÖh8ß7Ã™!-=y²Ï×£'d/§V"½Ê,â*S‚æß/n¿Úí×v¾â`ŸŒ¿=zeÆû&ñ£G¨ýœ­–²ˆ/j%òéU”—
>;Ûã¥"™ÓIÊª‹}.J†ì’¬Öâ=M•ù$/ä‚+.Ó‹Æ'1Kh™êZñß¥0p1c×TDîB)£‰¿<#0XVØTÆHxDÞ’)pRdçLÄªŒjv•OA4ùŠ:3¦ƒoª¾v™¦rI+<b¨qÄ”"Z’	1‘"]=cþj,&hÞùüKhÁ
Õ_¡Š¤\iÐ–TL¹ ãáùðŸ½€ë¿r8¦xà~03–õ`L0¯,VDH0
°ü-8Ž0wqÛ
.0àJÍ@|ñfˆá°V
p¡Ù”øa…`©W–,hZ2Â)‹‡d9ãÑÞ—4Àáóñ‹—ÄÚD>B 4gYk×%Õ`”!aB•ÀC!Ç±XA„ÔxœÁO© \«02à5ˆLP*˜_)Y‘Ï ˆF >%9ƒ#Bó”y“ ]IEÿ¢Hp !PZ‚…×ÉëƒŠ£°ïÇómìû£”šöäk_0‡8?Ae€žˆ¼4øD ß#M|š²Ì	OÈ˜@Á*¬ ¹`´æ:Q2-A†å/HRL£B·²rZÐŒiVŒÈ{Dì†fyÊ†H¢_¹(oHdÎPCrvQ
ð
‘òŒk¼6ž‡ìÏ%°ž/nébOD=D™M€~0üŒG…T4V¡	qŒ‚ãÝ¸ŒGäÇñ?Áwœ,ø…à´¡²ºàw^4¾HÓKi¿rÞ)o¥ožQ¯¤Ì®@a]È´'ÿFò·Œ¼—$Ié”<ã,xþƒ †p‰VPx(æÊ¾Gî|,5bîæØ9OS”VÉOàûÔñârâäÄ ýdþTÍ••OµfY®‘t ¤*3Qnz7!®¼2S¹¡øžg‹9;uŠÄdbs„Y%mÄ†OÜßèÁ^98ÍÏ;`>&[¹1ÀÔ ¨†ž1¯™*'j¥`¯pÎJ0{ÀlØ2r>ªó‰µÀôó-7ÎÝ³»+»ÿ“5¨ýÎLúªÍ¬+¯|ÊE€ûd¥™zæHTê³ÉYÃ@ sÁ§à] ôŒ¨bkÁÓ‡œ*¤TÈƒ¶òÇæµ…jIóœÖS¤+Eî
€Ÿ :Jj«º	cÙ­i;Ä-8ãy¹	µEïÔÂ©ÿ‚Ú³¤úÊ.û*¡ìœ0 Žº°ê…þï‰µ#V9[CÂ$syµd|:ëéµ—€ðwÈ‚çäÃGbJM.m­;"ÿB0/ H½dL >&å=€F®‚XQÖ|îðÑ29ï}È¥4-NÑñ½pb3Fã¨¶BËTâS,ë–à°à‹+ÌÇœù °Ã;S³È#yqbÂŒ	$“02¡Ñ<ôe[ýe0VÙl„ýƒžù†Üš¦ÓÓ›”¡D}úõÛ³Câ—X«9l.1‘ËbÂuA:ø"¬À`‰½ÄÃ†C‹„SAË 2°èÎå^+6"Ÿ$T•\.ða 9.À½Ã
fÂftÁA\£'!íõ„.$‡r™
(4ö"ˆåSd’ýFX2.3D1xN©˜^D~3‡p¤â95T3Ú¾©¹‡mX°Ó‹û‰ù!›`âÚKåìØÍ¨Ï³–2¦µoÁ¸‰”÷Ö9ÿÉY¶úd_~³ã~bEmÉêëÜrô‰”p)±æì±\
oTpé
$°MnpaË â‡ÿyÆáF	ÎÅ\A¡MÔL–il"En&âÐÙ1%œ0?°Ø0À‡ö7n÷ÀUôÿg$ø_ o¹Ž;à^kaÝeò(§š•©®î|ÐÊÇKA8]Ò•Á¹DxÉõ5DÐëë‘-ê óûùÝûË/¿~j,g2m¬ú¹k‚ ¯ã(Ô÷Vu1“ /q‰…ˆCQZæ.þ+S(æÿl4ª
¦ËF[FXS‚žQ_\«2'4µˆ²iAÎyoÍØ[¯Õµ]
ƒ¦ÈÅ|yqíF8òŸÃÂU‡zn¬‡ef-n‡d&/ì#×ŠÔ—äÔŠØ®Ùum{à6Ì¶Éëc[¼Mæh,zz=]ã¦W@ÕÜÒÖls0k3 ÿSÂ„‰˜˜ÚÔ.’4H`¿w °ðÈñ¼sÝsµ/V¹e¾Å‡‡dRV9\ByjjøÛâR	“AlNÅ‚ƒÃ»·_KÊÔº¦tÊçÀ»2ËIÂSf»±!i“:ƒ3\ô$2¶€í¾¬PŒ'«“#êÃäâ¥à6¦†ó÷ñÂLÖà ·2JÛ KbÊ`&‡8ÖŒv@DKîÄ‡jLÍ½Œ:6A o¤
f«”uœQêH,Xì¾³.àÉa,fZ? T¯rÅ x9¤™¦®ËÀêÄø•âÌ,ùÆ>ç`Ik‚LB„†¯¸¶Q '“¥Ð¶,ÂØ‹Î‘àÆ	<¯ YºBÍœ[F#²¤…¡Æùj½>D{éÚ¡Ü˜/œz¡j·œÉÛgK`¯zrã…=(Á¼°71Ú¬/ÏyÞÌ{·j&°•"ÄÀ¿ªdåßŽ"Ó@Ò’§³H‹22]0¦Y96ò6mFµq/¢!§ÊæŒå¸6(ó•©ûÃî-Õ¶ì­õ¶ú>N5GÔ³ê8¬
¬~›=va@xÍ°±éäà˜³°Ãßœ»‡ â‹w—îÍ(Vjl“Lê1î»©ƒ’œar÷Écé_ÕŒ4´9Þb
é#O5êñéòw7#ùE•JSJâî¬Ë4½Kš[&@VÚY½9ÿY`8‰RqW„IEom³mwò–é‰ˆæ=KÁRÛ=À~ª!t{­l–†;.ØÍz­ŽNxŒ[@ª­Y¥Æ96Ç¬Åb§¶T@e§ªº='fÂ,•+»N8”J9ØÃÅæzÛ3'vuÊ3ÝÆ>oW5:J‘Ì2B˜áG·ûˆØ‹p»›[Þi„¯MÑjDÞ…, ;›RãSQ”¹B˜ËNxln!çRé½îèÑÄ.ï¼5<¶ P÷ÐíÁñÖ3Æ°•ÛÁíº8]Ûî.·ØXlw·ÓÜíð€\Í-Kìâf~Ã6ë±ÚÍ½v…ªÂ [ÉÓ+?”WU{ßíT2oåS=P;¹ÔN8y65Á`•«¾4;niv/p÷!ÒÇ¾J;j•ÖÇNuZþÅãlíœñ»\±‡ð(µ[K7ü'ìKƒÃ–qíœ¯EqÐ„º‡í ]K‡ë[_Ü¸¸këjí«†¾Î;p×ÚÏ¶ad WÎ‚§÷ÃžÃ ¶œqÍTN#v¦Þ»·£ùŸóà­$¸Ý 8fÌ®ØÒ(b€IQ
Ñü9=%ƒÀÒƒª\¸¬™êßäøËõÅø×ß>}mö3¿ŽF£gæžæo»«Uf\›»dÊ|¨4nW+ó¿¯LSšùŽ» \EÚï•Vë[S*J ‰¶î{ëùsPþœ{:qÇä=oÊcãVœ¹‡)¸.œ¹“)·dù¶6òEæ=]L™?t¶$Ø81AÒºÑ•ÙïtrlyHåChÅŠ+Ý7ÕrÚ¬õÐí¡Š¨-¾ãšQ[è<ÌqÑW»ºØpÇ%Y/¥¥»õ íìl»a"ÑÒÑð7ßöŽr=n»8[mGÿúÞÎIjët=€{q¼Ýñ[G¤½Â=|»ûŸ5ãÜþëà}=zûp¾ÝÀ[Ã£¥ë•ylïâÛƒ·‹ëUfÜÕõ¬ ¶®×£·×Û¼5<º,Ëö%ú‘º+-—h[ék‹„=ŠÇi´´]´íŒbßs9RÏ¥ƒn-àï ½Çï°í—.Ø¿¾säNL7lUÊßåqš2]\²#–}æ¨ý™nN¹µÆ¿ÿÈƒ·j:zd û®ÍQ»6²[åß7pŽÚÀéâm€¬ëË~+Õa·RU†~Ø[6ï Ä¦=x®:êÉsxò€™¿{/^ ®#uºìÅ['Ã6âØ ÕsçðÜ±–>È³F‰mšŒÎ{ž@ÆÐ'ÀŸ&!¶ÑÇ–,=Ïké Ð%Z0ÛP=…ŽB!4õip¨AŠM$Šf,.Oð;‡ª›Ííngë7¸E³Î
ÐëOêž‡fÍ6‚ï‡Õr©ŠçæÅÏc=Ç2fIªÇšúóP ¦7p„ø\4žÿôùúx<¾ÏÆçðïO;ÇG2=’Çãæè_ÝQ]ÁKñøÛÆ ‘Ñ›«˜¥ô(Ï¨ ÛÌç/_ôÙ†qio®ßàÀ7)V$ã¢ÔöÉø	|ú>t-IèDâ#*g<šYd±÷èž‘Ô|LéúÍ}®ýíñAø°%ò©z¬>ò‚Œo™ÝxæÞ
¤šç)ÝŽS¶`)¡Ó‚±Œ	weO'û¸•@L‚Ð ëVÁÃ¼ðªþ	›6ÞÁè-kk·r4I5¨Ou£ŠÈ=ú/ë¨qü‰     0707010005c85e000081b40000000000000000000000015ab2175400000e7b000000660000000000000000000000000000004000000000root/usr/share/doc/opensvc/template.service.sync.netapp.conf.gz   ‹      í][oÛ8~ï¯ ZÜb×évö!ÓÍn;À`g¦tºûP	-Q6a‰Ôˆ”öÇï9¼H”“ØR}™x!?ä¢Ë!y¾ïÜHZzölŸŸ'ÏÈ?(N­D4$z•3"˜¦y¾›¸=~@Ü~u÷äöÙøë“Ìx/ÝˆŸ<ÁžÏÙj)‹ø¢î@ÂSVÀ™³=~PE*’9¤¬jê·¢dx¢`¿—¼`ñy!\q)¸˜š“?ÒT™31Kh™êº×¿JaNp1c×TD®”Ñ„¼%3Fcß	F`À¬°çUÎ"žð®™2÷FF-Ð‚ŠàŠßfŒüj	bôU2]à8ÈL*MÍ)‹ÉdE4\-dÌF„¼çIÂ
&tÀ#A‘ˆ
2aD1MJ+Âh437‚$«‘c.~‡M€¡¦·#èÜ¶#—p§|ÃœêÙ1 ¬ yÌ~²§™2z]È´Ìðvò».#Z’¸àÐ° yÆ‹ÎHaZÜ¬g þ(¦²W=ªhœ^Ó¨k c¥f$’B°H×ôV# î%j&ËLˆÖ¢‹RikW x…Bh1¥êûÉ’BzÆ•iÌ€fZ½ôƒ|È>¦×Q^ª?ÕËU íËDZ#†‚s&bUŽ
F5»Î§ š|Á>m {•¦r	.«Xðˆa-’L¸ˆÁ^Rëü\k€ªwDþ?	-BXëK¨")
WP1eä‚Œ‡çÃWx`|öÊX Å	ðÄƒ™±¬óp`‚ze±2J,	Ž#ÌÁEÜ¶‚¸V3ßC¼b‚vÁŠ \h6ÅÀÿOV–úÎ’MKF¬‡d9ãŒ”ª¤)ð Ÿ_½&Vÿ#ò“ 4  @s6‘%°Fq]RJ&T	\0r‹%DHmàž…,ˆkzlƒÈ¥bž¥dYDKaF#Ÿ’œÁ¡Á÷{• ]IEÿ¡Hpw$t£ðýP£Ú©8Jû¾?ßÆ¾ßK©iO¾¶þsHñ)TèÐ³0	âüiâ]Ð´ó„|€	¬Ü
Fkî¡HÄKaù‹)$ÔÐ¡;Y9- ™Ö*1ï¾¥Yž²!’èg.Ê[™;Ôœƒ×£¬B¤<ãÛ¦Àóý¹Öcê¸Ö{£)Êlôƒág<*¤b` ±
Uˆ3U èËxD¾÷®q²à?‚P‡Êö¯yÕ¸"LŸ–Ò^rÞ)o¥oŽ¨×Rf×Ða]È´'ÿFò·ô¼W$Ié”<c<NPƒ»D-(<seÿFî|,5bîbìœ§¶,÷¬©ã+øåÄÉ‰Aú‹!ÑTÍ••OµfY®‘tÐI…Åzà@”ïÆ%CË+Ê½Å¿y–±˜Ã°S×‘ªÚýøñ—@í¤õØpÆýì;·ù¸êc ²•ƒ-}CÏ˜ï™*'jÅ@öƒ)×¬– z@´l9ÕùDÈZ`úùŽãîÙÝ•ÝŠÿÁÔþ`‚¾jõïä•Ï¹pŸ¬4S/‰J}ö19kØ ôÈ\ð)XÈ€~FT±5ç‡éCNR*ä†AÛ†ü±ùla‡ZÒ<ç„õéJ‘ûà'€Ž’Z«.`¢/»¶CÜ‚;.ÉëM¨-z£~Fý„ìÐ–m$Õ-»ì«„²sÂ€D8êÂv/´O¬Eè°ÊÙê&éœËë%ãÓYO¯½8„¿C<'?}„!¦ÔäÒV»#òoó€ÔKÆàcRÞs hä*Ø€Õ`Íy‡oˆ–ÉyB(¥iqzˆŽ÷6‡Û-S‰O±¬[‚Á‚-®0sêÇŽ‹#X³È#y~bÂô	$“¸®1¡Ñ<´e[ýe0VÙl„ó=/ò’<š¦ÓÓÊP¢>ÿòõÅ!qƒ&Öj;—ÈU1áº üï£ü4½u‡	×-È<ÂÊN½ÓºW mD>I¨.*¹\$`Ã0@³Ye	+˜	›Ñqbœ„´í]Hå2Phœ/ˆÀ—O‘IöŠ°*d43D1xO©˜^D>Š™C8Rñ’ª™Þ^VÈ<À6¬@ØéyˆýøüMx§vŽ¥2vœÍ¨ï³š2ªµn^AsÆrš­ÎìËŽ!ºñ8î+ö–ü·nçŽ¡O¤„¦Äš±Çr)¼RÁ¤+œÃ6¹Àe›UÉ#¿yúáF	ÎÅ\A¡]/Ðá’5âÐØ1%œ0Xlà]û¥Ç$pýÿ	þô­"×q\Àj!Ð­»LÞ åàT³R#uÂÕŸ´òþ’ƒN—t¥@p.^rsôæfd‹:ÈüÞøñêóÏ¿5–3™6Vý\› È÷qö÷Nu1“Ð_â‡¢´ÌÿW¦PÌÿÛ˜¨*˜.mi¡ŸQ_Ü¨2šZDÙ´ gŠ¼?q«Å[ß«».L‘‹ùòâÆpäÏÃÂU‡:6ÖÃ2Q‹Û!™à…óÈuGêæ9uGì¬ÙM­{à.Ìv’×OŒm±6™£²èéÍé£0sTÍ-mÍ6.³&ð8ò¿$LÄÄÔ¦v‘t AÒ ç{	
 Á:×-·Qûb•[æ[lxH&e•Ã%”§¦†¿+.•bs+Ôè8{ûY±¤L¢kJ§|¼+3»ÊÎÆ†¤MêÎpÑ“ÈØ¶‡².@1ž¬NŽ¨“‹WÖƒ[ŸÆï!NÄ¬Â¡ßÊtÚ:XS‘üXÓÛ-¹ïª15÷2jßŽ¾‘*ØÍo—no6¨K ±`±û
ì–8G£13õBõ*çPŠ—³@š™4ÂUC`Ù X»Rœ™%ßÝç4iUIðÐp‰›6
äd²Ú–Eè{Ñ8Ü8aö6	€,]aÏŒ-£YÒÂPã‚¼‡ÚE¯Ñ6]”ó…ë^Øµ;Æäõ³Å±W=9ƒñÂ•½`^ØmÖ—ç<oæ½€[5HãØJbà?U²òG‘†j iÉSÜ3ÍBdˆÌ,Ó¬œëy›Ž6£Ú˜ÀgÑSe	sÆr\”ùÊÔýáì-Õ¶ì­ûmôƒ;EÏªã³*Ðú]ödØ…a›9`}ÓÉÁÿ8£°ÃßÜ»§â‹w—îÍ(VjlÁ‚zŒûn tP’3Lî>y,ý§ŠHC›ã-¦>òTc?>]ýê"’_T©zbJIÜu•¦÷IsËÈJÕ›ñÏ
ÅI”Š»"L*zg;˜Fp7o	LD4ïYz–ÚÙ»Óº‚Ðíµ²Yî¸`·ëµ:n8á1n©¶f”÷Ø³‹3µ¥*‹8mPÕí91+†`©\ÙuÂÐÁ¡TÊî.Gî˜ö¶gNìú”#Ý¡¾:qÚÌ2tB˜áG·ûˆØ-‹p»›[Þi¸¯MÞjD>à÷+ ;›RãSQ”¹\˜ËNxlù²‹TºG¯;z4±Ëã{o-èÔ=t{0¼õŒ1œÊí`v]Œ®Çmw“Ûl,¶›ÛiîvxD¦æ–%v13¿¿a›‰õXíf^»BUaÐÆ¬äé•Êªª…½o6*™·²©¨Lj'œ<›šàÆ@ÐÊu_š·4{¸‡i‹c_¥µJkc§:­âq
¶vÆøM¦ØCx”Ú­¥~‹ö¥ÁaË¸vÆ×¢8hBÝÃvÐŠ®¥Áu‚­/î\Üµ5µöUC_ç¸ÎkmgÛ02Ðk§ÁÓûbÏa[Î¸f*§;SŸ]ÛÑü×yðQÜî 3fWlñY€IQ
Ñü:=%ƒ@Óƒª\¸ª™/êßæøËõÅø7ß>cö3¿ŽF£æ™æ»«Uf\›§dÊ|¨4nW+ó®LSšùžŽ{ 4È"í÷J«õ­)%€D[÷½õü9(N„=¸cò€ž7åÑq+Î<À”@\ÎÜË”;²üÛ€ù"óž.¦‹Ì;[lL´®Dtmö;[Sùj±âJ÷MEµœ6ëD=t{¨"jï¸fÔ:s\ôÕß®&:ÜqIÖKiin=h;Ûn˜…H´44üÎ·}¢\Û.ÆVëÑ¾5Â9Im®p/†·;~ëˆ´7@øÕÃ·»ýY5îÁüàWëëÑÛ‡ñíÞ-M¯Ìcûß¼]L¯Rã®¦gµ5½½}˜ÞÎà­áÑeY¶/Ñ4»Òr‰¶e‘¾¶HØ£xœ‰–¶‹¶Qìç\Ž4çÒÁ·ð÷€ÞãwØé—.Ø¿~&æÈ31Ì°U)ü=–Ç™”éb’±ìçgŽ:?ÓÍ(·Öø÷ãßyð©šŽÙÈ~Öæ¨³6²[åßOàu§‹A¶²®/û­T‡ÝJU)úqoÙ¼‡›öà¹ê¨'ÏáÉjþæ½x¸ŽÔé²oÛˆcTÏÃsÇjú<Ï%¶1hR0:ï	txEŸ š„ØF[²ôü9<¬¦O€@k”hÁ œ†ê)t
¡ªOƒCRl"Q4cqy‚/Ü9TÝlw;[À-ªuV@¿þ î}hVm#¸>¬–KU¼4(~Ëè%–1#àHR½ÖÔß‡5½…Và{Ñ,xþì%ùòt<¾ÏÆçðóÝßÎñ•LO‡äéø•9úWwT—@ðR<ýºÑIdôö:f)=Êë3*Èöóùë×}·a\Ú‡ë78ðYàCŠÉ¸(µ}ó¾Dßþƒ/FM:‘øŠÊfYœ{tïHj¾¦tý‰Ææ9×þqÈø¢|ÙùT½V_yÎÆ‚Ìn¼soRÍó”GŽ‡îIÇ)[°”ÐiÁXÆ„{Ž²§“}ÝJ &Ah€u«àe^ØªÃ¦õw0zËšÁÚ£œMRjÇSÃ×¨"rOþ,€k×þ   0707010005c85f000081b40000000000000000000000015ab2175400000dae000000660000000000000000000000000000003f00000000root/usr/share/doc/opensvc/template.service.sync.rados.conf.gz    ‹      í]YÛ8~Ï¯ œ`ÝŽ;›Ù‡ÎéÝI€`Ž,d÷!ºi‰²	K¤F¤ìö`üVñ(w·-ÅÇ¶òCâÖQ$ëûªXU¤¥'Oöùyô„ìñƒâÔJDC¢W9#¥ÚQÜ? n¿º{ôûdüíÑ+3Þ7vÄaÇçlµ”E|Q·ŸO¯¢¼TpîlÔ‘ŠdN')«û\”Oì’¬îÅ{š*s&/ä‚+.Ó‹Æ™˜%´LuÝñß¥0'¸˜±‚k*"×PÊhâ›gË
{Bå,â	È[2enŠ¬àœ‰XÕ‚£‚QÍ®ò)ˆ&_±ÏßŒêàJÁe—i*—D±bÁ#†=Ž˜RDK2á"&R¤+¢gÌ·Æb‚ê‘À¿„,`O}	U$åJCo,1eä‚Œ‡çÃx`|öÚåp8ñÀý`f,ëÁ<˜ ^Y¬ˆ ``ù[paî.â¶\`À•šøâÍÃa)¬:À…fSV4àÿ…‚¥¾³dAÓ’®H©X<$Ëfð½¤)ð Ÿ_¼$Vÿ#òA  9›ÈX£¸.©¥	ª.
9ŽÅ"¤Æ6pÏRA¸V¡gÀ6ˆLP*¨_)Y‘Ï ˆF >%9ƒ#Bó”y• ]IEÿ¡Hp !PZB7
ß$¯w*ŽRÀ¾Ï·±ïRjÚ“¯­AÀR`ü@
:ôDä¥Á'øiâ]Ð´eNxB~À
VnÈ£5÷Ð‰’i	2,A’b;tK` +§Í˜fÅˆ¼GÄnh–§lˆ$ú•‹ò†Dæ5$gçàõ¨ «)Ï¸Æ¶)ð<d.õ|q«/öFì‡(³	Ð†Ÿñ¨ŠÆ*TA ŽQ0<Ð—ñˆü8þá'¸ÆÉ‚¿P@*Û¼æEãŠ@š^J{mÈyCd¤¼•¾yF½’2»‚ëB¦=ù7’¿¥ç½$IJ§äégÁógè5¸KÔ‚ÂC1Wö;rçc©s7ÇÎyš¢´J~×SÇWðË‰“ƒôgôS5WV>Õše¹FÒA'U™YˆrÓ»qÉÐòÊLåÞ‡âwže,æ0ìÔu$&#|üø[ ÈvÒzl8ãþFöƒÛü¼êc ²•L€jHàó=SåD­ãŽÂi	f˜­ [FÎGu<²˜~¾…ãÆ¸{vwe·â²µß™I_µ™õoÅ•O¹pŸ¬4SÏ‰J}ö19kØ ôÈ\ð)XÈ€~FT±5ç‡áCNR*ä†AÛNùcóÙÂµ¤yÎë)Ò•"w9ÀO %µVÝ„‰¾ìÖ´âÜñ†¼Ü„Ú¢7êaÔAÈmÙFRÝ²‹¾JH;'H„£.l÷Bû÷ÄZ„³œ­.a’Î¹¼Z2>õôÚ‹Cø;DÁsòá#1¥&–¶Ú‘!˜ ¤^2& òž@#—Á¬¨kÎ;|C´LÌ{²@)M‹ÓCt|/œ€ØŒÑ¸ª­Ð2™øÓº%,Øâ
ã1§>pìðÍäìâHžŸ˜°=DÁ$‡Lh4mÙfŒUc4aýÀ çE¾!÷‚¦éôô&eHQŸ~ýöì¸Ak9‡­Ã%Fc#rYL¸.(Bƒ‡è,±öƒxXwh‘p]Ð2€Ì#¬€z†s¹ïH‘O²‹J.	Ø0ÐÀ`Þa3a3ºà ®‘Œ“¶ý¡É!]¦R õ‚|ù™d¯Ó¡BfA3Cƒ÷”j€áEäg1sG*žSC5ÓÛ72÷°3vzb?>?dL¼S[c©Œ«õ}VSFµö+(7‘ò>Å:ã?9ÍVgöeÇ0»ñ8î'Vì-ùOÝÎ-CŸH	M‰5cåRx¥‚IW 9‡mb+€Kö 8üÏ“Ð7Rp.æ
m¢f²Lcã)r37€ÆŽ!á„ùÉ€Å†Þµ¿qã¸‡.£ÿ?#Áÿ}«ÈuÜ°ZCtë.’7@98Õ¬ÔHpuçƒVÞ_rpÂé’®Î%ÂK®¯Áƒ^_lR‘ßÏïÞ_~ùõóÐPc9“icÕÏµ	‚|Gaoe3	ý%.°q(JËÜùe2 ÅüŸBUÁtYÀhËsJèçE”Å×ªŒÃ	M-¢lZ3E^ÃWA3öÖ÷êÚ.…Säb¾¼¸v#ùó0‚pÕ¡žëa™Y‹Û!™ÉëÈuGêæ9uGlÕìºÖŠ=pf[äõ…±-Ö&sT=½š®1
S+ jnikv8˜5‡éÿ)aÂDLLnjI$°Þ;HPØ xäøÖ¹n¹Ü³Ü2ßbÃC2)«.¡<59ümq©„É 6·bBÎaÕÛ/Š%ej]S:åsà]™å$á)³ÕØ´IÁ.z’[Àv_Ô(Æ“ÕÉõarñÒzpëSÃù{ˆ…xa¦«pè·2¶–Ä”ÁL~¬éí€ˆ–Ü‰wÕš{µoGß&Ìf)ë8c¨ 1a±û
Ìº€'‡Ñ˜)ý€P½Êyƒäå,fŠF¸j, «cWBŠ3³ä£ûœƒ&­
2	.qe£@N&K¡mZ„¾#Áx_) ²t…=KpnÈ’†ägÈ]ôúmÓµA¹1_¸î…]»eL^?[{ÅÐ“3/ìAÙÆ5€½ñÑf}yÎófÜ¸Uƒ4Ž­!þS+ÿvi¨‚–<å5@è\”‘©‚Á0ÍÊ¹°ž·éh3ª	|9U”0g,ÇµA™¯LÞVo©¶ioÝoë ïãTsD=«ŽÃª@ë·Ùa„mlæ€õM'ÿÃœ…þæÞ=8Ÿ¼»poF1S+``+˜Ì`RqßL”äƒ»OKÿ©f¤¡ñSyª±Ÿ.w3’_T©zbRIÜu™¦wIsËÈJ;«7ç?+'Q*îŠ0¡è­í`¶ŒànÞ2ý1Ñ¼gé!Xj«XR5„n¯•ÒpÇ»YÏÕqÃ	qHµ5Ë Ô¸ÇÆ˜µX¬Ô–
¨,â´AU·çÄ¬@‚¥re×	C‡R)}Ø¤Ø´·=rbW§<Óm¬óvåP£¢É,C'„~Tp»ˆÝ°·»¹å†ûÚä­Fä]QÈ°³!5®1E™ëÀ…¹è¤Çær.•îÑëŽMìòøÞÁ[Ãcz uÝo=bK¹Ì®‹Ñõ¸ínrû€­Åvs;ÍÝÈÔÜ²Ä.fæ÷7l3±«ÝÌkW¨*Ú˜•<½ôãAYUµ°÷ÝF%óV6Õµ“Ií„“G`3PÜZ¹êS³ã¦f÷w"mqì³´£fi-pì”§5àïQ<NÂÖÎ¿Ë{’»µ4Ãï1Â>58l×ÎøZ$M¨{ØšÑµ4¸N°õÉÝ“»¶¦Ö>kèó¼çy­ílfAzå4xz?ì9bË×Lå4bgŠá³+p;šÿ9>J‚Û= €cÆìŠ-"˜¥ÍŸÓS24=¨Ò…ËZù¡þMŽ¿°\_Œ]ðøíÓ×f?óÛáh4zfž9`þ¶»ZeÆµyJÆ Ì‡Jãvµ2øçÊ4¥™ßé¸Ç @ƒ,Ò~¯´ZßšRQH´uß[ÏŸƒòçDØÓ‰;&èysPÞ·âÌ=L	ÄuáÌL¹%Ëï°h°‘/2ïér`ºÈü¡³% ÁÆ‰	‚Ö•ˆ®Ì~§“cËCJB-V\é¾©¨–Óf¨‡nYD­ñ×ŒÚBçaŽ‹>ûÛÕä@‡;.Éz)-Í­mgcÛ³‰–††¿ù¶O”ëqÛÅØj=úÏ÷ÎpNR[£ëÜ‹áíŽß:"íþëáÛÝþ¬÷`~ð_ëëÑÛ‡ñíÞ-M¯Ìcûß¼]L¯Rã®¦gµ5½½}˜ÞÎà­áÑeY¶OÑT]i¹DÛ2I_[$ìQ<N¡¥í¢mgûšË‘j.Ìpkè=~‡-¿t1Àøõ•˜#Wb:˜a«Tþø{,S”éb’±ìë3G­Ït3Ê­9þÝø÷@¼TÓÑ"; ÙWmŽZµé`Ý2ÿ¾€sÔNƒld_ö[©»•ªRôÃÞ²y!6íÁsÙQOžÃ“ÔüÝ{ñq©Óe/Þ:¶Ç:¨ž;‡çŽÕô	xž5JlcÐ¤`tÞèð2Š>þ4	±>6eéùsxþXMŸ Ö(Ñ‚AX†ê)t
¡ªOƒCRl"Q4cqy‚/Ü9TÞlw;[À-ªuV@¿þ¤î}hVm#¸>Ì–KU<7(~Ëè9¦1#àHR½ÖÔß‡5½Và{Ñ,xþìòõñx|1ŸÏáßŸþvŽ¯dz<$Ç/ÌÑ¿º£º‚—âñ·N"£7W1KéQ^ŸQA¶˜Ï_¾<è»ãÒ>\¿Á/R¬HÆE©í›/ð%øö|é4j’Ð‰ÄWTÎx4³ÈbíÑ½#©ùšÒõ'›ç\ûÇ!ã‹ðeKäSõZ-|å8#>2»ñÎ½H!4ÏS9º'§lÁRB§cî9ÊžNöu+˜¡Ö­‚—ya«þ›ÖßÁè-kkr4I5¨Ou_£ŠÈ=ú/Ìý‰    0707010005c860000081b40000000000000000000000015ab2175400001093000000660000000000000000000000000000003f00000000root/usr/share/doc/opensvc/template.service.sync.rsync.conf.gz    ‹      í]msÛ6þž_I¦£xNVä\zÜ&S'if2M›Nœô>d26D‚N$Á euîÇß.^(P²%*z‰Ô£>$2_À>Ï.vùèÑ6?‘-~PœšæQ—èiÁˆÄïŠÛâÄmWw>ã õ¿<øÁŒ÷¹ñƒØñ1›N„ŒÏgí+ÁñÓ-~P?*¤¬jè£,žìÏ’K/œ(¤¸áŠ‹œçCsòM•9³„–©žõù7‘›<1É5Í#×NÊhB^£±ï#0\&íyU°ˆ'<‚k†,‡{qè$ù“ü”+m´M«Ž_ŠRF )!zÄ—zäÍÉ€J&#®™*hÄN+¨¤šÅF^Ÿð”)èBÀ™˜KE
ª\FÕ)WDQ¼d*«†2FQI™–Çðtžtz¨,aoÇ«-¯3èVA‡Œ$BB÷5å©‚[ü 8!î#BŒƒÞ=*XŽ	à¯™Ò<§:QG½	@– ¦f þ—¤©²ÿsŒÀZ)h:#S®‰\ hä¿$–…ùÚÌ¤áHòÜ	öœ5eÃ°#Æo Ö	mQƒ>I¤È’¿xÀ‹·‚&™%
“7¼ÜZzKd™£žzäòâ7¢´¿‘bÒ1wqY	DÈ…Fïbšú¸«@Â‡×oNS>fÀ!¦ÜÈªî÷ÊéªY&Û¬Âîw‰ú¶t«Î€Ox› ´µI¡£™Ð tQæš‚çºKÞ,¿üã™ð4%À>žLT#)BHl+ ]p7’E2š¦S2èz†Ðˆ9×½¹ì‘ÈÄÈL9¬ÒÎ!p­¢	ÓS’Û"t!IÅÄp¦%­¼¹$3:EfI!ô‰÷;‰ZîyTN‹o$|ª3»r<Ø[ð;U;Ng 4•ßÃ"·Ï±FË)žAuƒ'2ÌB‚eûCõ0Àëç¨…8Åø/¶æÃP¢àÀo‡qïÔ3Ü‹'Ø*v"*VŸN>z}Kæ@PeQ©•Qv'f)^¡ª;¨LgÐðÍLhÆZ„1"^^Ád¸0ñÈÐÓ„lV@n3*Á$ºyt
ð&(âJK>2Ù!ì–E%
ê™N;­À½áÐuò8ïbBÓ@ÚP
œÑ€¨¾WjÖ-×#ÐçÝ—™51°µžÒL4Ý0&Âî˜ž0–C7Amµ^"m;ï&¯!º«û¨*
ìNËÖ[G)»­ñõbyÚ‚gU–RæÑˆæCCR—©H~œ<¢)°ãàÌÅ«w…8ÈRƒFšÛ¯@‡Ð“”gü‡ºß<ö¸HØ-<€ò%ØÊ„ÇzDŒÒàòËKB‹"å1ÅÚäCKš+HNzä£à²¬ÐÓº‘³ì8B‹µòÀMÐ1„ÆÅ1Ëq~N‘Ž©cæ3hôóëŸß\|z÷ñ‹Ù]WÃ«¨(ÏV—Â¼¾1ƒÙ?V3ÁÆ›²«bhŠ}þR7Û#:Ÿa@#¦ÌúÁ€ƒÃyê&*ÆDõöÈ«çyw
ñ£5{I$Z99'ýîY÷)èŸ>E0N &¸ÌŒe-˜»Ô+ ”3sièŽ#Ìkƒ‹¸­pe’Öâå7ôÛ¿0™³´
ÀnhZ2vÝªD©J“Âá³þÓgvÑ ú-Lª šÓd‹1Q\—f¦í‚#WåÜ’D,€ ˜B^š³Sr­BÏ0r+\ ÃYÏÚhfrè)É5O™W	Ð•TäðŠ·s€nHß»ÞU£°ïû³Uìû³š¶äkê_0‡?A…Î =yað‰@„_ ô.ÈÆù<!ß`+·ä‚Ñš{è@‰´–¿˜fÚp`A`=k¥Ó‡¼GÄniV¤‰Þñ¼¼%‘¹CuÉé™Yd«ÈM<mcn²Ò®q¹n®/öFìG^f ?ã‘ŠÆ*TA Q0<Ðq|ßÿî'Lx¬,ø…àÔ¡²}ÁkžÖ®óœ‰°×†œ7DFÊ[éËgÔ+!²+è°–"mÉ¿”ü=ïIRÈ¼÷q<;±«t,G-à^‰¹²ß‘;ïK“¹9vÌÓ´–aã*>u|íáêŽ•ƒôHÊ©++ŸjÑ6’:©ÊÌzàpá×6a\2´<5S¹÷¡øg‹9;u‰ÉÀÆïßÿ²´Î¸¿Ñ‚}çà6?ï€ú¨lêÆ S ZKÔ™ï™*jª`?à(œ–`ö€Ù
°eä¬7‹'BÖÓÏVp|oùÞß‹ÝŠÿÅjÔþÙLúªÉ¬¿W>æy€û`ª™:q$*õéûä´fwÜÂÈ€~FT±9ç‡á."àîGÀŸÂ”ß7ŸìPHgyÂZŠ¬K‘»à%@GÉL«nÂD_¶0m‡¸w<'Ï–¡vÓõAõ?²][¶]ý®ZvÑ—]¦á¨¥í^hÿžX7¡GÀ,g¥K¤c.®&ŒG-½¶â^B<&oßÃSjbi«ÝùÁ<¯VêÏú&ä=€z.ƒXQÖœwø†h™˜÷>dRšÊãC´¿ï_“‰1­wúÌî´Ûƒ‚8’gLù½™‚IÜ›ÐhÚ²Íþ2«Æh6ªVê½Èçä^ÐŽrgRÔÇŸ¿œì·ùÂ‹jo5Ö#rÀµ¤îgÙ­}¿èVò-®µEz°èÎå¾WfsñR@vQÉåy6Œ÷s,Á¼Ã¦Úù«%ãXrbÛïz#8¤Ë4‡@ãzÙ2E
æŠ0òÛË¶™.ŠÁ{JÕ!f_ÁÍbæ)UxBÃ]=Ì}…ì§ê }~È&˜x‡v¥2vS˜RÝg5e7øÍ×å;1ÎøN³‡YäqáÙ$÷Jeñ$ç°Mlp™Ò1UòˆÃÿ<	ýp-çùXA¢íëÓÐSØÒ²ÐØ]á˜óÜ,6ð®}E•‡Ëèÿf$øè[EÎã¸€ÕB [w‘¼ÊÁ©F¥Fê„»;oµòþ’ƒN'tŠÅbX\‚®¯Áƒ^_÷lR‘ŸÛ…íjLF¢V.Ô&ú>öÂþ.d#ý%.°ÈãP”…óÿÊd Šù?kU’éRÂhËsJèçy”Åç×ªŒÃ	MÝDÙP’SE~„¯9ÍØß«k».L‘óñäüÚ°çÏÃÂ]‡ÙÜ8–™µ¸’«eè™5È™uÄ®š]Ï´b,Âì
yÝÂØ
k³eôøÖtQ˜µªÆ–¶¦Éì	¦Cþ]À„‰˜˜ÜÔn’v° ¯ƒë½…u€GŽ¿`ó–[Ë}1Ë-‹6Ü%ƒ²ŠáÊS“Ã/ŠKÅKkðVWoƒõ=òI1,,GEÏ(mJã2+ì/ÌjlHÚdÁµ6—`Àv_Ô(ÆƒéÑõ0¹xa=¸õ©áüÝÅ…øÜLVáÐoe:­\­e0“ƒ«{; ¢%wâ]5†æ^ÆÌ7£¯…
X_n‚é9œ±Ô˜°Øº³/àÉa4f–~@¨®
ÀØi Í,á®!°¬¬NŒ]å"?u¥ïà'Ç I«S%—¸e£@Ž/ƒ¾«®¹Ì²Ô&8·ôzdB¥¡Æ9y¹‹ž¢mzfPnÌç®{a×ŒÉëg…c¯ztã…”½\Úriã£Íþò˜õ¸p«i[™‡øO¬üÛQ¤¦Z°bNa³03Df†ivÎsëyëŽ6£Ú˜À§¼&§ŠÆŒ¸7(ŠiU’íã+ªmÚ;ë·uÐ÷qª>¢–UûaU õEödØ„aË9`}ÓÑÁ˜³°ÃßÜ»§â“wîðG9©„ám`R±î¦J
†ÁÝ¥ÇÒª©kc¼›!„<ÕØüÙ˜‘ü¦JÕ“JbuÖEšÞ%Ím +í¬^Ÿÿ¬@Pœ@©XaBÑ…r0»Œàn^1ý±<j-´–ÚÕWÐí!tµV6JÃŠv;Ÿ«cÁ	±¤*Í2(Õî±1æL¬²¿Ñ€l9NkTu5'fÂ,S»O:8”J9èÃ&Å¦½Õ‘»:æ™nW?=¼ mf:!Œð#Ém‘ý)«ýÈÃ·Ì[õÈÏR
	ØÙ÷˜¤,¸0ÔðXQÌ/”nÑ[=ó›­]€7‡Ç
ô êº-Þ|Ä.å®avë]‹Ûæ&·ØjX¬6·ã¬v8 SsÛ›˜™¯oXeb-V›™×¦PU41+q|éÇAYUµ±÷ÕF%ŠF6Õµ‘Im„“G`9P,­\µ©Ù~S³{»‘¦8¶YÚ^³´8®•§ÕàoQÜOÂÖÌ¿Ê[÷’»54Ã¯1Â65Øm×Ìø$u¨[ØvšÑ54¸µ`k“»'wMM­yÖÐæy;ÎóÛÙ*Ì‚ôÊiðø~Ø³Ä–>*%Ám à˜1÷ˆÐ(b€‰{òk˜N éN•.\Ì™êßâÔÔüfü’Ç/ÿhê™_t{½Þ‰yæ€ùÛVµŠŒkó”ŒNYt•Ærµ²èTO¬IÓÁóÿ Ai_+­æKS*J,yj[ËŸýðçHØ³wLÐòf§¼1:nÄ™{˜ˆ[‡3w2eA–¯°h°”/¢hé²cºˆâÐÙ`éÄAë4®üóá‹-‡”>„Z¬¸²~QÑLN“}¢º-d3o¸gÔ:s,ÛìoS“n¸%ë¥44·´m3ÌB$þæÛ>Q®Åmc›éÑ¾v†s’š]àVosüæin€ø¢µ¾íÏªqæ‡½on}-zÛ0¾ÍÀ›Ã£¡é•ElŸâÛ‚·‰éUjÜÔô¬ ¦¦×¢·ÓÛ¼9<ÖÙ–mSô=­®4Ü¢m˜¤Ïm¶(îg¡¥é¦íÚ(¶k.{ZsYÃW&ðw€Þâ·Ûå—upüÚ•˜=¯Ä¬a†Rù;ào±ÜÏ¢Ì:&¹&–íúÌ^×gÖ3Ê•9þÝø·@î|©fM‹\ÈvÕf¯«6käz™»€³×œu²	³ü²-¥Úm)U¥èÃ.Ù¼ƒËjð\vÔ’g÷ä5u-^ nMê¬S‹7O†UÄ±ªåÎî¹c5}žgŽ«4ŒŽ[íž@FÑGÀŸ:!VÑÇ¦,-vÏ«é# Ð%0—¡Z
í…B¨êãàPËHX\áwv•7›ÇÝŽæp‹jIè×_Ô½Íª­×‡Ùr©äóˆâ'±ˆž`ÓŽ$ÕkMý}(PÓ[h aß‹fÁógŸ“Ïûýó~ÿ´ÿþô¯3|%ÓÃ.yØjŽþÓÕ%¼Ì~Yê$2z{³”îåõd[ùìÙ³¾Û0.íÃõkø”ãCŠÉx^jûæ|‰¾ý_:š$t ð•#,²¸öèÞ‘TMéüÍs®ýãñEø²%rY½V_yÎÆ‚Ì®½so
R-Š”GŽ‡îIÇ)»a)¡CÉXÆr÷eO'ûº•@L‚Ð ë¦ÁË¼°Uÿ†Mëï`ô–5¹G9wê¤êÌOu_£ŠÈ=øàºŒ•™   0707010005c861000081b40000000000000000000000015ab2175400000f5e000000660000000000000000000000000000003c00000000root/usr/share/doc/opensvc/template.service.sync.s3.conf.gz   ‹      í][sÛ6~Ï¯À$ÓQÓ•i+M÷Á­3õî¶;™m›q³ûÉØ	I“€’ÕÙ¿çàB‚²e‘Ñ%VK=$2/Àù¾s òÅ‹]~ž½ ;ü 8µÌã!ÑË‚õõöâvøq»ÕÝ³8ØgŸ}kÆ{#~ö{}Ë–!“óºq•S	'NvøAí¨Xtœ²ª¥_eÉð„d¿•\²º?ÒT™3…s®¸Èy>=oœIØ„–©®{ý‹ÈÍ	žÏ˜äšæ±k(etBÞ£‰ï#0`&íyU°˜Ox×LY÷ÆF-Ð‚ŠáŠ_gŒüó—÷DSiC&<edR¦))¨žEæ‚ú„ÒFBt}W@ŒŒišPMIÎX‚W	’Bsèt,YÆrMS¼eLÓ4"o'FŒbrÎch’òTÂÄœAf›K¸ºUöêº'3Q¦	‡}’lH”Ào l„å"a$†îÄ"×</™‘ö-^Æ)‹@?ßšv.È©’óÓl©æñéœÊSäX4ŠÖ‘KÆŸ•[þÄç¢Aí‚¶ù)WºÁ¹+QJ@|šŠ±"TÑ” Í7=SÈ*ãŸ3óÕp3YFóÄ‚#ã&6ZˆT…hQ¨¯ÖA$
zQnÐ§wVQÀÂ{Š‰"è¶ÁÄ)p99awqZ‚­}Ëx
ã2¾eúOm++n8§Üb½âÕ×NA¨õ²HµNwLãÛ²€ƒFéî’ 4Ë÷'Ù˜¿NãèÔ¯U<cI™²?7ûW4ï•‚Ú7¡ÏªYE&…º.ˆjŒÐØRBÑ’O§o’ ÐÐ8†fa$ˆ‚%™²žnà€-I€Y39„À˜˜ø6ˆã–	q)%D'bºQ’l(4ð$4Î<”„8G ÑrÁ•vI@§ïG¯_ˆ*óuT*¦×qQþÁ\hß™00¸DÕ‚AÁ€Ïu1Ñäöùcƒe—i*U®=Ž™2nÌ"O—6½±­³EõFäïð/ò- ³¾â%ºl„[Ò|ÊÈ99Ž†¯ðÀÙÉ+Öáâõ`f,ëÁÜ˜ ^!—&UQ€åÏÁqãVº‚‹¸mpméâÇ!~ 'â¹fàçðÿ‹Éœ¥¾³dNS(%¸"%$IC²˜ñxßK(r–xxtöêµ-dÀ ßæ„ hNÆ¢Ö(®KŠñeH ”—’5Š£D Ar¡±œÁ³XWé°b2m`©{lð±Ä.è)â¼¼S	Ð•TäðŠ·Ÿ	¾*ªŠ£°ï›Ñ&öýV
M{òµõ/˜C
ŒH Bg€žÒ–D€OŒù€K4¼šJ±‡/Âè¯j·ä‚Ñš{èX‰´–¿ IA*	º'0Uåù»£Y‘BÑ$ú	*ê;›;ÔœŒÀëQÈ–Ê<å×Ø6&<!û¬Ç*o¥/öFìG^fcLh&$ã±Š&*TA ŽQ0<ÐID¾9ûâ{¸ÆÉ‚¿P@*Û¼æUãŠ@š^{mÈyCd¤¼•þxD½"»Æ¹)Òžü’¿¥ç½$“”NÉ—gG/Ñ	jp—¨…‡®ìwäÎ»R#æ.ÆÞò4eád•)a_Íd”•“€ô—C(ríŒÈ§Z³¬0…tR•™õÀy/Ó„qÉÐòÒ„rïCñ;Ï2–pvê:’±ÍÞ½û9d;i=6œq£ûÎÁm>î€ú¨léÆ ¡P	<c¾gª«¥‚a|kæä¬– z@´lEu>²˜>ÚÀqcÜ=»»²[ñßYƒÚ?˜ ¯ÚDý{yå—<¬öÆKÍÔKG¢RŸ¼›œ4l ú¯ëúûSÅVœ¦8íã§wB´mÈ?3ŸìPZ<a=EºRä!xÐQRkÕLôe÷Âvˆ[pÇyýjóÞ¨Ÿ„Qÿ!Û·eÛÉ¯ªe—}•PvŽG-m÷Bû÷Äš‡«œ.aœÞrq½`|:ëéµ‡ð7È‚oÉÛw0Ä”š\Új7"ÿA0ÏH½`,|LÊ;€"WÁ¬¨kÎ;|C´LÎ»Y ”¦òø=ÛÙ|rK´L%>Å²n¶¸Ä|Ì©{5ay$ÏÀOŒÙ=Ä’IœWÆÉÛÐ–mõ—ÁX5f³f~Ú çE^µ i:=¾ %ê—>¾Ü'nŠé•šÃÎÃMŒÆ"r)Ç\KŠÐÁßf=œeéÈ¬;´H¸.h@æVÀ =ÃXî{Ò"r% º¨äò|640'Ì;¬`ÆlFçÄ5Šq2È…m@è\p(—i%€Æù‚|ù™d¯Ë!)² ™!ŠÁ{J5Àô"öQÌÂ‘æ§ÔPÍôö¢BfÝ²8T ŸyµïóùüMx§ysuÈ¬qW÷YMÙunó”;bbñf«3»²cˆn×È|`ÅÞ’ÿÕíÜ3ô±ÐT¾bì‰Xä^©`ÒHÎa›Ü
à2{ITÉcÿ»•º
ÿÑ3žß*(´ƒÝ*P©c n0 SÂ1óÁ€ÙÞµ_¸q¬!«èÿ`$øè[E®â¸€ÕB [w™¼ÊÁ©f¥Fê„«;oµòþ’ƒNt©@p!^rsôæ&²Ed~ÿøáÇË÷?ý:4ÔXÌDÚXõsm‚ ßÇ(ìï½êb& ¿Ä%y¸L­´(üZ¶© T½´NTI¦K	£-c¬)¡Ÿçq–œß¨2	ššÇÙT’E¾ƒ¯¸…áïÕ]
¦ÈùíâüÆ0òçaáªCëa™¨ÅíLðÂyäº#uóœº#vÖì¦ÖŠ=pf;Éë'Æ6X›ÝêBoN×…™+ êÖÒÖì5kOÓ!ÿ[@ÀDLLmjI$p¾w0Aaà‘ã/Xçªå6j_¬rËbƒÉ¸¬r8Ü=hjøûâRÁ 1·ºR¸Í0"ï›”©QtMé”ßïÊ¬0»íllHÚIÁû©(NÁ¶€m]Ö(&ãåÑõirñÒzpëSÃø=Ä‰øÜ„«pè·2¶–$”A$?Öôv@DKî‰wÕ˜š{µoGßHpÏ«I¦WpÆP—@bÁb÷˜uO£13õBõ²à1PŠ—“@š™4ÂUC`Ù X=1v•‹ü$ÜŽ{îT	ðÐp‰›6
äd¢Ìµ-‹Ð÷¢qLpãÞWæ YºÄžM0¶DYPi¨qNþµ‹^¢mº6(7æs×½ÆNáUcòúÙàØ+†ÁxaOÊ^®ìîEã£Íúò-/šy/àVÒ8¶21ðŸ*Yù¯£HC5´)Ç¬RgYÆf†iVÎsëy›Ž6£Ú˜Àû¼!§Ên+pmPK¿³Ê¯¨¶eoÝoë ×qª9¢žU‡aU õûì	È°Â6ç€õMGÿÓŒÂsïœŠ/Þ]º7£X©IØ‚õ÷Ý@è ¤`˜Ü]y,ý§ŠHC›ãÍ§>òTc?®.qÉ/ªT=1¥$îÎºLÓ‡¤¹ed¥êÍøg‚âJÅ]&½·Ìý`ÂÞ¼!ü±<¦EÏÒ}°ÔÎà|ª!t{­l–†;.ØÝj­ŽNx‚[@ª­Y¥Æ=6Ç¬ÅâLm©€Êy’6¨êöœ˜C°T,í:aèàP*å [›ö6gNìú˜#Ý¾~\pé·„ÕªŠ%·ûˆØ‹q»›[Þi¸¯Ç¼UD~RH…?À0…kLR–…\˜ËNx<>…\¥{ôº£G'vy|çà­à±=€º‡n†·š1†S¹Ì®‹Ñõ¸mor»€­Åfs;ÎÝOÈÔÜ²Ä6fæ÷7l2±«íÌk[¨*Ú˜•8¾òãIYUµ°÷ÉF%ŠV6Õµ•Im…“Gàq Æ¸1´rÝ—f‡-ÍÖ·‘¶8öUÚA«´8vªÓð÷(¦`kgŒŸdŠ=„©ÝZšá§a_ì·Œkg|-Šƒ&Ô=l{­èZ\'ØúânÏÅ][Sk_5ôuÞžë¼Öv¶	³ ½v<¾öì±ÅŒk¦
³ÅðÙ¸Íÿœ%Áí À1cvÅ–Æ1Ld™çÍŸÓS24=¨Ê…ËZù¡þ]¿°\]ŒÿNòäÍ—ß™ýÌo†Q½4Ï0Û]­"ãÚ<%cPC¥q»ZYüsešÒÌïtÜc  Ak¿WZ­nM©($Ú¸ï­çÏ^ùs$ìéÄ“ô¼Ù+oŒŽ[qfSq]8ó SîÉò;l<ÊQôtÙ3]DñÔÙàÑÀI+>Óìw::¶<¥ò!ÔbÅ•î›Šj9mÖ‰zèvPEÔßrÍ¨-tæDöÕß¶&:ÜrIÖKiin=h[Ûv˜…H´44üÍ·}¢\Û6ÆVëÑ>5Â9Im®p'†·=~«ˆ´7@ø¯‡o{û³jÜùÁ¬¯GoÆ·x+x´4=ûn…¼-M¯Rã¶¦çÞuÑÒôzôvaz[ƒ·‚G—eÙ¾D?ÐìJË%Ú–EúÊ"aâa&ZÚ.ÚvF±Ÿs9ÐœK3ÜXÀ? zß~§_º`üú™˜ÏÄt0ÃV¥üð÷XfR¦‹IvÄ²ŸŸ9èüL7£ÜXã?ŒäÞ§j:Zd ûY›ƒÎÚt0Èn•?sÐ	œ.ÙÈº¾ì·Ríw+U¥è§½eóB<¶ÏUG=yöOPó'ïÅÄu¤N—½x«dØDë zîìŸ;VÓGàyV(±‰AcÉèmO ýÈ(úøÓ$Ä&úØ’¥çÏþùc5}Z¡Dá4TO¡ƒPU}jâ1Å3–”GøÂ}ÕÍæq·³ÕÜ¢Zgúõ;uïC³j‹àú°Z.•<5(>MD|ŠeL™T¯5õ÷¡@Mï 	“ø^4ž?{A><?;;?;;9Á¿ßÿu„¯dz>$ÏÏ^™£_»£º‚—ùó:‰ŒÞ]',¥y}FÙN`½~½×w&¥}¸~ƒïs|H±"ÏKmß|/‘À·ÿàK§Q“„Ž¾¢rÆã™EçÝ;’š¯)]}¢±yÎµ2¾è_¶D®ª×já+/ÀÁXð‘Ùwî-A
¡E‘òØñÐ=é8es–:•Œe,wÏQöt²¯[	ÄL`Ý2x™¶êß°iýŒÞ²f°ò(çA“TƒÚñTÇð5ªˆÜ³ÿcSiÿY’    0707010005c862000081b40000000000000000000000015ab2175400000ed4000000660000000000000000000000000000004200000000root/usr/share/doc/opensvc/template.service.sync.symclone.conf.gz ‹      í][Û6~Ï¯ N°Ç“¦}˜f‚N·-lÛ,fû3´DÙ„%Q%){\ìßsx‘(ÏŒ-Å—ŒòÃ\t9"Ï÷I‹OŸîóóä)ÙãÅ©U‰^þÌ¢TälqûmÝ~u÷ä#vöéøÓ“ïM/«?y‚mŸ³ÕRÈø¢nB$rÅ•f¹†Óg{ü ¦T$
:IYõ¼?dÉð„d•\²º!¿ÐT™3…®¸Èy>½hœ‰YBËT×m÷Âx>c’kšGîA)£	yCfŒÆ¾Œ@·™´çUÁ"žð®™²îð2ÐÄ‚É@þD”ÅÁÓU§>(FÎjÁ³kNE"Ëh«Üñ}pÍ¥mé Àí<>†î+EÞQ¾WdÝÿŽ”Ú—îý¾AcpžI"¢gÆd3¦%¿%TJº"3¡4´Ïž¥Œ¥Må”éÀ<c¶àS¤ \ÂOÁsÍb2Y‘920(å@‰á#™ÒB²kÍ3&ÊÓ³’oÆãCI\Jª¡!£·<+3²¤\T(XÄ¬¥h}£ÑÌr
Ž‰ÒT3ËšÎ ìÅƒ E L÷!«ß&@MoFÈÜ…åK{¸{`I!:4 ùsÆ5SØ™b•FåxzBïÍ^+½¹x+ýÆ‹Ó"t}’/Yrðq…f¥Î^IÆã¿_àÏsó÷Kó÷7b‹burÆõÅ’§0"òÊCYý»ã›³‚bzå—5šÏQ÷F«é ç˜R©Z°ó7ÅS®ØæO_¥©XÅ$Ú	¶¬Í‚L8d"OW6c°OËBõŽÈ?á'$,°žúªœýI"i>eä‚Œ‡çÃ—x`|öÒ iqB{‚ƒ™±¬óp`‚z…\‘\€€Q€åoÁq„¹3¸ˆÛVp×jâ{ˆ7C|ÿÄ´|Êdþ1™³Ô7–,hZ2q¬T,’åŒC^Rª’¦À8|>~ùŠXýÈÛœÐ€  ÍÙD”ÀÅuiò !a¹*†BŽc± ‚äBã3pÏÒœp­BÏ€ÏÀhR1¨º¨Jþ A4ñ))
&e^% *røE‚	Òš!};líØ °ïÛómìû«šöäkë_0‡1É1‚jKxir_Ä'ø7ÒÄ» ©eAxB¾À
VnÈ½5÷Ð‰i	2,A’bfÀàŽÀ@æ}Pî29"¿€#b·4+R6DýÊóò–Dæ5$gçàõhV‘§<ƒÔž…pÈþB ë1\k‹½Û‘—ÙÄÖÚ¤P› gâLA zã"‘oÇ_ÿ ×8Yð
Á¨CeÛ‚×¼l\HÓKa¯9oˆŒ”·Ò7GÔk!²kh°–"íÉ¿‘ü-=ïIR:%ÏÆÏŸ£Ôà.Q
Å\Ù¿‘;ïJSš¸;çiŠÒ*ùXPRÇWðË‰“ƒôçC¢©š++ŸjÍ²B#épÔ¬Ì¬D¹ðn\2<yeB¹÷¡ø7Ï2sèvêbFzL#ßý²´Î¸ÿÑ‚}ãà6w@}T¶r}€Ð ¨†ž1ß2UNÔJA7¾Ç^8-Aô€hØ2r>ªó‰µÀôó-7ÆÝ³»+»ÿ›5¨ý³	úªMÔ¿“W>ãy€ûduúsG¢RŸ½KÎ6 í2K>ëÐÎˆ*¶æü0}(¨BJ…Ü0hÛ?6Ÿ-ìPKZ<a=EºRä>ø £¤Öª˜~p¬¶CÜ‚;.É«M¨-z£~Fý„ìÐ–m$ÕOvÙW	eç„‰°×Ò6/´O¬Eè°ÊÙê&éœ‹ë%ãÓYO¯½8„!ž“·ï ‹)5¹´ÕîˆüÁ¼  õ’±ð1)ï9 4rl8$ë;kÎ;|C´LÎ»iŠÊÓCô ó÷¡e*ñ)–uK0X°Å•É0êÇîGÂIn&= — ‡H ™ÄIÅ	æ¡-Ûê/ƒ¾jÌf#?¨f¨Pä%y4M§§”¡D}öñÓóCâ¦˜^«9ü$jlD®ä„kI:ø<lŽÎÇ~ë-®	¹°²ó´nH‘÷ª‹J.Ï°aè ÙL›„Ì„Íè‚ƒ¸F1N¹°ÏºÊeš¯ìä‰À—O‘IöŠ°’"3D1xO©˜^D>Š™CØÓü5T3­½¬yhT ìô<Ä~|~È&¼ÓÜO,[cÇÑŒú>«);ýoþÜ<ÿïŒÿä4[Ù—Ctã1pÜVl-ùoýœVScWž-s¯T0é
$ç°Mnpá=@Å#¿yúáF	Îó¹‚B›¨™(ÓØxŠÂ.	€ÆŽ)á„ù`ÀbÃ ïÚ/]? «èÿÏHð%Ð·Š\Çp«5„@·î2y”ƒSÍJÔ	gwÞjåý%'œ.é
'»ð’›ð 77#[ÔAæ÷ÓÏ¿\}øõ¡¡Ær&ÒÆ¬Ÿ{&òm…í½S]Ì´—¸Ä"CQZÎÿ+S(æÿmTI¦K	½-#¬)¡Q_Ü¨2šZDÙT’3œû_D9ÍØßª;.L‘‹ùòâÆõpäÏCÂY‡:6ÖÝ2Q‹Û.™à…ãÈuCêÇrê†ØQ³›Z+öÀ]˜í ¯Ûbm¢@eÑÓÓ5FaÆ
¨š[Úšµ§fNàq:ä˜ˆ‰©Mí$é@ƒ¤Ž÷6 9þšÕMËmÔ¾Xå–Å’IYåp	å©©áïŠKƒØÜŠ5:‡ŽÞ~P,)S£èšÒ)ŸïÊ¬ 	O™I›Ôœá¢'™[ˆ±¶‡².@1žœÞª˜ÇÉÅ+ëÁ­Oã÷âs¬Â¡ÝÊ4Ú:XS‘üXÓÛ¸ÉxW©¹—Qû&pôTaÂl•²Ž3¶€º»®ÀÌxr™¡ªW€bP¼œÒÌ ÎËÀêÄØU.ò33å£ûœƒ&­
2.qÃFœL”fQ+à{Ñ8\8÷•9@–®°e	Æ–Ñˆ,©4Ô¸ ?Aí¢×»h]”ëó…k^Ø´;Æäõ³Å±W=9ƒÙ÷2²½Øæ5€½ñÑf~yÎ‹fÞ¸U4Ž­ÌCü§JVþti¨’–"å˜5@ê,ËÈŒ‚A7í²Rëy›Ž6£Ú˜À‡¼!§ÊæŒ87hÖÇ%ÍÑ[ª©['çÛ½y¥\³G=«ŽÃª@ëwÙa„ÏØò
ã›NþÇ…þæÞ=8_¼»toF±R“Ð±3ê1®»ÐAIÁ0¹{ï±ôŸ*"mŽ·˜BúÈSíxõ»‹H~R¥j‰)%quÖUšÞ'ÍM +mToÆ?+'P*®Š0©èå`vÁÝ¼%ü±<¢EÏÒC°ÔŽ4—»ûµV6KÃìv½V7_ÓŠq	Hµ4Ë Ô¸Çæ˜µX©-P9ÓUÝš3a–Š•'J¥ôa‹bó¼í™»>åHw¨ï‹]ùoÝaµª"Éí:"vË"\îæ¦wîk“·‘Ÿ¥°³)5Î1IY:pa~ÿu3>mB.„Ò=zÝÑ£‰ß;xkxlA î¡Ûƒá­gŒáPn³ëbt=n»›Ü>`k`±ÝÜNsµÃ#257-±‹™ùõÛL¬Çj7óÚª
ƒ6f%N¯üxTVUMì}¶Q‰¢•Mõ@ídR;áäØÔ‚V®ûÒì¸¥ÙƒÀ=„H[û*í¨UZ;Õiø{S°µ3ÆÏ2ÅÂ£Ôn-ÍðsŒ°/[Æµ3¾ÅAê¶ƒVt-®l}qwàâ®­©µ¯ú:ïÀu^k;Û†Y^;žÞ{ƒØrÓ;ËðUÜ® 3fgli1ÀD–yÞü:=%ƒ@Óƒª\¸ª™/êßøËõÉø×’Çož½6ë™ßG£ÑsóÎó¿]Õ*2®Í[2e1T—«•ÅÀ¿W¦)Í|OÇ½ È¢êõwj}iJE	 ÑÖuo=ÊŸaO'î˜< çÍAyctÜŠ30%×…3÷2åŽ,¿Â6 ÁF¾ˆ¢§Ëé"ŠÇÎ–€$­«<º6ëNŽ-©|µXq¥û¢¢ZN›y¢º=TµÆwœ3j‡9–}õ·«Éwœ’õRZš[ÚÎÆ¶f!-Í½O¾ÇmGc«õè?Ÿáœ¤¶F×¸ÃÛ¿uDÚ üêáÛÝþ¬÷`~ð«ƒõõèíÃøvo–¦W±}‹oÞ.¦W©qWÓ³‚Úš^Þ>LogðÖðè2-Û—èG]i9EÛ²H_›$ìQ<Î@KÛIÛÎ(öc.Gsé`†[ø{@ïñ;ìðKì€_?sä‘˜fØª”¿þËãÊt1ÉŽXöã3GŸéf”[küûñï<øPMG‹ì d?jsÔQ›Ù­òïpŽ:€ÓÅ Û Y×—ýRªÃ.¥ªý¸—lÞCˆMkð\uÔ“çðä5öZ¼@\GêtY‹·N†mÄ±ªçÎá¹c5}žgÛ4‘ŒÎ{ž@FÑ'ÀŸ&!¶ÑÇ–,=Ï«é Ð%Z0‡¡z
…B¨êÓàPƒ›HÍX\žà†;‡ª›Íëngë/¸EµÎ$´ëoêöC³jÁõaµ\*ùÂ¼¢øE,¢XÆŒ€#Iµ­©¿jzH˜Ä}Ñ,xþì%ùøÕx|1ŸÏáçßã–L_ÉWã—æè7î¨.àeþÕ§N"£·×1KéQ¶Ï¨ ÛÌç¯^toÃ¸´/×opàCŽ/)V$ãy©íÎ¸‰îþƒ›N£&	Ü¢rÆ£™EÇÝIÍmJ×ßhlÞsí_‡Œ=àfKä}µ­nyÎú‚¯Ìnì¹·)„EÊ#ÇC÷¦ã”-XJèT2–±Ü½GÙÓÉn·ˆI`Ý*ØÌŸêwØ´þzoY3X{•ó IªAíxªc¸*"÷ä‘Ïf‡³’  0707010005c863000081b40000000000000000000000015ab2175400000ebc000000660000000000000000000000000000004100000000root/usr/share/doc/opensvc/template.service.sync.symsnap.conf.gz  ‹      í][Û¶~Ï¯ Np¼Ž7MûàdƒnO 8ms€$§A°KK´MXU’²×Åùñg†‰òîÚR|Éú@~Ø‹.#r¾o†3CZ|òdŸŸGOÈ?(N­²¨Oô*gðgª2šï$n·_Ý=úŒ}2üòè•éï…ïñ£GØô9[-…ŒGU"‘)®4Ë4œ>Ûã¥"‘ÓqÂÊç}”Ã’ýUpÉª†¼¥‰2gr)\q‘ñl:ª‰Ù„‰®Úî…ñlÆ$×4‹ÜƒF'ä™1ûV0ÝfÒžW9‹ø„GpÍ”epo„—&LòÇB€¤Ì(ž®"8õI1rVéž
Ž‘1¦4‹Õ îx\sa[z p;¡ûR‘·”ïÙB÷@‡÷¦û@¿ïbÐœg’ˆ	Ñ3c±)Ó’ß*%]‘™PÚgÏ‰BFŒ€Ò‰¦rÊt`1[ðˆ)’S.á§à™f1¯HÏé”Œò ‰÷á#™ÒB²+ÍS&ŠÓ³’ï‡ÃCI\Hª¡!5§7<-R²¤\T(XÄ¬¥h}£ÑÌr
Ž‰ÒT3ËšÎ ìÅ½ E Lw!«ß:@uoFÈÜ7…å[{¸;`I`t¨AòçŒk¦r±3År*Êñ*ô„Þ›½V2z3z+ýÆ‹Ó"t}’/Yrðq…f¥Î^A†ÃŸŽðç¹ùû…ùûû{1œ^EyñmQüãÚcüb–3Û+ÁÎ ò)Æ Ÿ±Í_jp^&‰XÅ$‡-ø'2æ0\‰,YÙ!Ì> FõÈ?á'Œt,€³º„*GI$Í¦ŒŒÈ°Þ†g/¼'ÜfÊÒÌÃ	êrE2–¿ÇæÖà"n[Á\©ˆï Þñ~ãÄ)“5øÿÅdÆßX² IÁ8ÖB±¸O–3e¡
š àðùðÅKbõ? ï2B 4gcQ k×…˜û„eª .
9ŽÅ’	Ï À<K3Âµ
=>‡Š^Þ¹yòÑÄ'$gpBê„y• ]IIÿ¡Hp !PZ@3¤o‡Mfj”öýp¾}BÓŽ|Mýæ"&ZCPmN)M0†øD ÿFšx4•¢È	Ÿï0‚¥[rAoÍ=t¬DR€Ë_¤˜É`o	da ù“ò»¡iž°>’è7ž7$2w¨>9;¯G3°Š,á)Ä2ðlÌÌBöçXÁÉZ[ìØŽ¬HÇ6ùKy$…b` ±
Uˆ3*è‹x@~~÷\ãdÁ( •m^ó¢vE M/…½6ä¼!2RÞJß<¢^	‘^AƒµIGþäoèy/É$¡Sòtˆ£àù3t‚Ü%jAá¡˜+û7rç}abe7ÆÎy’ ´R>f8ÔñüòÄÉ‰Aú³>ÑTÍ••Oµfi®‘tXÆ)RëQnx7.ž¼2C¹÷¡ø7OSsèvâbJ¦‘ïÙFZgÜÿhÁ¾qp›w@}T¶r}€¡P	<c¾eª«•‚n¼Â^8-Áè£`ËÈù Š'BÖÓÏ·pÜwÇî¶ìVüoV£ö¯fÐWMFý[qåSž¸W8>s$*ôÙûÉYÍ ý@fÉ§`] ÚQÅÖœ†9UH©m;äÍg;Ô’æ9Ï@XG‘¶¹Ë~ è(©´êL_­©Û!nÁäå&ÔQ?£þBvhË6’ª'»è«€´sÌ€DØki›Ú¿'Ö"ô˜ålu	ãdÎÅÕ’ñé¬£×^ÂÏÏÉ»÷ÐÅ„šXÚjw@þƒ`Ž H½d,|LÈ{ \Ö}gÍy‡oˆ–‰y7MŠPyzˆ´Ð~Z&ŸbZ·ƒ[\ÙÒºQ8v_š%™©Â+@o‚bÁ$Îri4mÙf)ôUc4aý œ2A‘ä^Ð4žÞ )êÓÏ_ž7ÅôZÎá«î¨±¹”c®%Eèàð°:K¬ý ÖZ$\jÅx°²yZµ
¤ÈÙE)—g°aè ÙÔñÃfÌftÁA\-'½LØç÷]é2ÍVv¶†DàË§È${E˜I‘é£¼§P=/"?Š™CØÓì95T3­½(‘¹o^2vzb?>?d¼ÓÌÏtZcÇjFuŸÕ”6nžvÆrš-ÏìËŽatã1pÜ¬ØZòßê9t\Bx¶Ì¼RÁ¤KœÃ6±À…%{€ŠG~óIè‡k)8Ïæ
m¢f¢Hbã)r»B!d ;†„cæx×~áúq	\FÿF‚o¾Uä:î€X­!ºuÉ œjVh¤N8»óN+ï/98ádIW8ûš„—\_ƒ½¾Ø¤"¿_~}{ùé·}CåL$µY?÷LäÛ8Û{+»˜	h/qE‡¢´ÈÿW&PÌÿ[+TI¦	½-"Ì)¡£(G×ªˆÃM-¢t*ÉNF/¢Œ¦ìoÕµ
¦Èh¾]»üyèA8ëPU·Ì¨Åm—Ìà…uäª!Õã9UClÕìºÒŠ=pf[äõ…±-Ö&rT=½š®1
S+ jnikÖBš9‡éÿ-`ÀDLLnj'I{$õ°ÞÛ› °ðÈñ¬sÝrk¹/f¹E¾Å†ûd\”1Ü„òÄäð·Å%ƒØÜŠ	5:‡Vo?)6)£èŠÒ	ŸïŠ4'ž0[I;©"8ÃEO2·^pl÷E]€b<^Q&/­·>5¿ûXˆÏÌÐ`íV¦ÑÖÁ’˜2ÉÁÕ½]Ž«B‘Üïª14÷2*ßŽ¾*Œ™ÍRÖqÆP@bÂb×˜yO£1Sú¡z•ó(ÉËY ÍpÖXÖVOŒ]e";3S¾1ºÏ9hÒª à¡áW6
ä¤¢0«,1Y ß‹Æ1Á…x_‘dÉ
[6Á±e0 K*5FäÈ]ôzí£+ƒr}¹æ…M»eL^?[{ÉÐ“3˜}/~Þ‹½`\Ømæ—ç<¯Ç½€[ÙIãØŠ,ÄÀÊ`åOG‘šj hÉŽQ„Î²ˆLºi×9ZÏ[w´)ÕÆ>e59e”0g,Ç¹A‘¯ÊuÇ>¾¢Ú¦½U»7/è®÷¨cÕqXhý6{2ìÂ€ð[õßtrð?ÌQØáoîÝƒSñÉ»÷f35	[Á`ƒzŒën`è $gÜ}ðXúO9"õmŒ·˜BøÈíøpù‡‘ü¤JÙ“Jâê¬Ë$¹Kš›&@VÚQ½>þY 8RqU„	Eo-³ewó–áeÍ;–‚¥¶zP_í×ZÙ(W\°›õ\Ý|o(Æ% åÒ,ƒRícVb±R[( r'5ªº5'fÂ,+;O:8”J9èÃ&ÅæyÛ#'vuÊ#Ý¡¾Àté¿†ÙªŠ$·ëˆØ‹p¹››Þ©¹¯MÞj@~•RHÀÎ†Ô8Ç$e‘ëÀ…¹è¤†Çær.”îÐkØéñ½ƒ·†Çô êº=ÞzÄ–r[˜]£ëpÛÝäö[‹íævš«©¹i‰]ÌÌ¯oØfbV»™×®P•41+qzéÇƒ²ªrbï«Jälªj'“Ú	'Àf Æ¸0´rÕ¥fÇMÍîî>DšâØeiGÍÒàØ*O«Áß¡xœ„­™1~•)v%wkh†_c„]jpØ4®™ñ5HêPw°4£khp­`ë’»'wMM­yÖÐåyÎóÛÙ6Ì‚ôÊiðô¾ØsÄ–›^¢…¯’àv à˜2;cK£ˆ&²È²ú×é)éšî•éÂe%È|Qÿ&ÇoX®OÆ¿–<~óôµYÏü¦?ž™w˜ÿíªV‘rmÞ’Ñ+ò¾Ò¸\­È{þ½2uiæ{:î5 ð@•ïcSëKSJJ ‰¶®{ëøsPþœ{ZqÇÄoÊ£ãFœ¹‡)¸6œ¹“)·dù¶6òEä]L‘?t¶$Ø80AÐºÊ¢+³ÞéäØòÒ‡P‹%WÚ/*ªä4™'ê ÛCQi|Ç9£¦Ðy˜cÙe»šèpÇ)Y/¥¡¹u íll»a"ÑÐÐÜÎ;Üv4¶Jþóµ#œ“ÔÔè: ÷bx»ã·ŽHs„_|»ÛŸUãÌ~µ°¾½}ßnà­áÑÐôŠ<¶oñíÀÛÅôJ5îjzVPSÓëÐÛ‡éíÞm¦e»ýHÕ•†S´“ôµIÂÅãZšNÚ¶F±«¹©æÒÂ·&ðw€ÞáwØòKl_W‰9r%¦…6Jåï€¿Ãò8E™6&ÙË®>sÔúL;£ÜšãßäÁK5--²]Õæ¨U›Ù.óï
8G-à´1È&@Vùe·”ê°K©JE?ì%›wbÓ<—uä9<y@Í_½/×’:mÖâ­“aq¬ƒê¸sxîXMŸ€çY£Ä6%£óŽ@‡'Qô	ð§Nˆmô±)KÇŸÃóÇjú´F‰Â2TG¡£PU}ª‘b‰¢‹‹ÜpçPy³yÝílý·¨Ö™„výMÝ~hVm¸>Ì–%Ÿ›W?EôÓ˜pdRnkêïCšÞÀ&Lâ¾h<ö‚|~<Ž†Ã³á9üüéÇsÜ’éqŸ<¾0G¿wGu/²Ç_6:‰”Þ\Å,¡GÙ>£„l/0Ÿ¿|yÐ½ãÂ¾\¿ÆO¾¤X‘”g…¶;_à&¸ûn:š$t,p‹ÊfY¬=º=’êÛ”®¿ÑØ¼çÚ¿7zÀÍ–È‡r[-Üòœô_™]ÛsoRÍó„GŽ‡îMÇ	[°„Ð©d,e™{²§“Ýn%3Ah€u«`3/|ªßaÓú;è½eMoíUÎ½:©z•ã)á6ªˆÜ£ÿÈyšìC‘  0707010005c864000081b40000000000000000000000015ab2175400000e55000000660000000000000000000000000000004200000000root/usr/share/doc/opensvc/template.service.sync.symsrdfs.conf.gz ‹      í]mÛ¸þž_A$88A½Ž7ÍõÃ^Ü¶¹ Aï’¢¹´‚`—–(›°DêHÊ^úã;CRåÝµ¥øåÖ­ü!ñêeHÎóÌpfHKOžìóóè	ÙãÅé•ˆ†Ä¬r_3­âDï"n¿½Û¯î}ÁÁ>}ôƒïëjÄaßçlµ”*¾¨» çygÎöøA%éHæt’²ª©_UÁð„b¿\±øÖ‰\É×\
.¦öä;šj{&f	-RS÷úƒö3¦¸¡"òí¤Œ&ä™1—`Ì”;¯sñ„GpÍ”	¸7²jtW¼‰Lˆ™1’Êˆ¦¨œŒÅoUŠ®ÈLj½³WÀ¹x:"¿Î¸&9U.dŠp0¤ÐMÓ•ëFLŒÄ‘pÁHÌ“„)&Œ—ËcM©‚ãBB—FÐ¹,@ d"åãé10¬yÈ ~ (Jk c¶à#S%‹œ,¡Eæ.…‚ÃT <TM™	às·hÀÈßÇâ‘xº°¸ÿs@‰¤X0ÈçÂ°)S÷‚õÏ·ï<>9åª²³u„J\,¨èÍPäÓ«(/ôêâ*ï0Ä,g"ÖµàH1jØU>Ñäöùk’Ë4•K¢™²=]kt^HÍââZ¿†ê‘¿Á¿h*Õ—PMR®ô–(*¦Œ\ñð|øŒÏ^ ’' ÜfÆ²ÌÃ	ê•jUÍ?UC¿Ç­Gì
.â¶\`À•žøâÍ·t¯gJ°´ì,YÐ´`£@dÂâ!Lƒ<šUQ
>¿xIœþGä½ 4  @s6‘°FsSPJ&tQÎ¤žc±‚i°œÁ³TnÂÈÈ¶~¤‚úµsòM§@|JrG„á)+Ut%9ÊE‚»àJB7TÙ7=4(ìûþ|û~+¤¡=ùÚúÌ#Æ¤@P¡3@OD^Z|"ß‘&¥r=OÈw˜@ÁÊ­ ¹`´ö:Ñ2-@†ã/HÒ@‡n	dU1ùˆ¼GÄnh–§lˆ$ú™‹â†Dö=$gçàõ¨ «)Ï¸Á¶1ÜÙŸK`=_Üê‹»û!Šlôƒág<RR30ÐX‡*Ä1
†zã2‘ïÇßý×xYð
Á¨Cíú‚×¼h\H3Ké®9o‰Œ”wÒ7Ï¨WRfWÐa£dÚ“#ù[zÞK’¤tJžŽq<†NÐ€»D-hbS=í¾#w>1÷sìœ§)J«äcjH=_Á/'^NÒŸ!*ÖsíäScX–$tR™óÀ(?½[—-¯ìT^úPüÎ!aŠ9;õ‰ÉÄÅ?þrtÎø¿Ñ‚ËÎÁmå¼êc ²•L€jHà+{¦‹‰^iÆ6_vZ‚Ùf+À–‘óQO„¬¦Ÿoá¸5îžÝ]Ù­ùï¬AíŸì¤¯ÛÌú·âÊ§\¸OV†égžD…9û˜œ5l€`ºéû¬d@?#ªÙšóÃð!§)rÃ¢í¦ü±ýla‡^Ò<ç„õéJ‘»à'€Ž’Z«~ÂD_vkÚqîxM^nBmÑõƒ0ê?!d‡¶l+©nÙG_¤¬Îá¨•ë^hÿ%±¡GÀ,g«K˜¤s.¯–ŒOg=½öâþ
Qðœ¼ÿCL©¥vGä_æ i–Œ	ÀÇ†¼ç ÐÈg°ª¬¬=ïñÑ²1ï½…Y¦U§‡èøØ…Y›‰O1­[‚Á‚-®0óêÇß\aâHžŸ˜°=DÁ$–n'4š‡¶ì²¿Æj0š°~à*¸^äkr/h†NOoR†õé—¯Ï‰›ÆåŠFÎáêp‰ÕØˆ\ª	7Š"tð7xXÎk?ˆ‡s‡	ß#ÈJ„50ÀÌp./{ÒFä“„ì¢’ËE6´0Ç
Ì;Ì`&lFÄ5’q2Òµ? t!qŒ
HÖ"ðåSd’»"L‡”Ì‚f†(ï)ô Ã‹¨œÅì!©xN-Õlo_WÈÜ·š;=±Ÿ²	&Þ©«±TÆŽÕŒú>§)·&f¿n^‰ñÆrš­ÎìËŽavã1p¼œX±·ä?u;·}"%4%ÖŒ=–KQ*LºÉ;l[\X²¨xÄáž„~¸‘‚s1×h=“E[O‘»e·hìNX9øUÑÒµ¿öã¸‡>£ÿ#Á¾Sä:î€X­%ºuÉ[ <œzV¤N¸ºóÞèÒ_rpÂé’®4Î%ÂK®¯Áƒ^_\R‘ßÛŸÞ]~þù×¡¥Ær&ÓÆªŸo•}…ý½•]Ì$ô—øÀBÄ¡(#sïÿµÍ 4+ÿlª3…‚Ñæ”ÐÏ‹(‹/®u‡š^DÙT‘3M^ÁWA3ö¦ìÕµ[
¦ÉÅ|yqíG8*ÏÃÂU‡zn¬‡eg-î†d'/¬#×©›äÔqU³ëZ+îÀm˜]‘·,Œm±6™£²èéÕt­QØZÕsG[»‹Ë®	<L‡ü	&bbsS·H:0 i€õÞA‚ÂÀ#Ï_°ÎuËmä¾˜åù’IQÅp	å©Íáo‹K%L±½jt¬Þ~Ö,)R«èšÒ)ŸïŠ,'	O™«Æ†¤MêÎr±$±l÷E]€b<YQ&/w>5œ¿‡Xˆvjp
‡~kÛiç`ILÌäÍ}lp3Ñ‘;)]5†æ¥ŒÚ7£o„
æ²”uœ±Ô˜°¸}v] $‡Õ˜-ý€P³Ê9îÈƒäå,f‹F¸j, «kWBŠ3»ä£ûœƒ&
2	.ñe£@N&a\Z„¾#Áx_! ²t…=KpnÈ’*Kòr³>D×tmP~Ì¾{a×nS©Ÿ-Ž½bèÉL)ìAÙÆ5€½õÑv}yÎófÜ¸Uƒ´Ž­!å§
Vþí)ÒP-yÊ1j€ÐY‘­‚Á0íÊ¹pž·éh3j¬	|9U”0g,ÇµA™¯ª=e|EK{ë~;}§š#êYuVZ¿Íž€»0 lc3œo:9øæ,ìñ·÷îÁ©”É»÷f35[Ád“zŒûn`ê $gÜ}*±,?ÕŒ4t1Þb
á#OöãÓå?#•‹*UOl*‰»³.Óô.i~™ Yéfõæüç‚â$JÅ]6½µÌo®v7o™þ˜ˆhÞ³ô,uÕ¬éB¿×ÊEi¸ã‚Ý¬çê¸á„Ç¸¤ÚšeQjÜãbÌZ,VjTqÚ ªßsbW ,ÁR¹rë„¡ƒC©”ƒ>\RlÛÛ9±«Sžéõ³‰KÐf–¡Â?RÜí#b7,Âín~y§á¾6y«ùI)© ;Rã“REnæ£“[6óKmzôº£G·<¾wðÖðØ‚@ÝC·Ã[ÃRn³ëbt=n»›Ü>`k`±ÝÜNs·Ã25¿,±‹™•û¶™XÕnæµ+TmÌJž^úñ ¬ªZØûf£’y+›êÚÉ¤vÂ©D`3PÜZ¹êS³ã¦f÷w"mqì³´£fi-pì”§5àïQ<NÂÖÎ¿É{’»µ4Ão1Â>58l×ÎøZ$M¨{ØšÑµ4¸N°õÉÝ“»¶¦Ö>kèó¼çy­ílfAzå5xz?ì9bË7Lç4bgšá³+p;Zùs|”w{  ÇŒ¹[E0Q…ÍŸÓS24=¨Ò…ËZý¡þMŽ¿°\_Œ¥xüæé+»ŸùÍp4=³Ï°»]­2ãÆ>%cPäCmp»Z‘ÊçÊ4¥ÙßéøÇ @ƒ,2å^i½¾5¥¢hë¾·ž?åÏ‰°§wlÐóæ ¼±:nÅ™{˜ˆëÂ™;™rKV¹Ã6 ÁF¾È¼§Ëé"ó‡Î–€'&ZW"º²ûNŽ-)}µXq¥û¦¢ZN›u¢º=dµÆw\3j]	s¬úìoW“î¸$[Jiin=h;Ûn˜…H´44üÍ·{¢\Û.ÆVë±ü|ëç%µ5ºÀ½Þîø­#ÒÞ á¿¾ÝíÏ©qæÿu°¾½}ßnà­áÑÒôŠ<vOñíÁÛÅô*5îjzNP[ÓëÑÛ‡éíÞ]–eûýHÕ•–K´-“ôµEÂÅãZÚ.ÚvF±¯¹©æÒÁ·&ðw€ÞãwØòKì€__‰9r%¦ƒ¶Jåï€¿Çò8E™.&ÙË¾>sÔúL7£ÜšãßäÁK5-²}Õæ¨U›Ù-óï8G-àt1È6@Öùe¿•ê°[©*E?ì-›wbÓ<Ÿõä9<y@Íß¼/×‘:]öâ­“aqœƒê¹sxî8MŸ€çY£Ä6M£óž@‡'Uô	ð§Iˆmôq)KÏŸÃóÇiú´F‰Â2TO¡£PU}jb‰¢‹‹|áÎ¡òfû¸ÛÙúnQ­3ýúú÷¡9µàú0[.´znQü<–ÑsLcFÀ‘¤z­iy
4ôH˜Â÷¢9ðÊ³¯É—ÇãñÅx|6>‡üË9¾’éñ<¿°Gÿìš^ˆÇ_7:‰ŒÞ\Å,¥Gy}FÙ^`>ùò ï6Œ÷pý>|H±&…qo¾À—HàÛð¥Ó¨IB'_Q9ãÑÌ!‹µGÿŽ¤ækJ×ŸhlŸs]>_ô€/["Ÿª×já+/ÀÁXð‘Ùwî­@
¡yžòÈóÐ?é8e–:UŒeLøç(—tr¯[	Ä$°n¼Ì[-ß°éüŒÞ±f°ö(çA“TƒÚñTÇð5ªˆÜ£ÿÛë:ý     0707010005c865000081b40000000000000000000000015ab2175400000f1a000000660000000000000000000000000000003d00000000root/usr/share/doc/opensvc/template.service.sync.zfs.conf.gz  ‹      í]mÛ6þž_A$(œà¼Ž7—Þ‡MtïÒ ÅµMÑ$w‚`—–(›gITIj½.îÇß_$Ê»¶¥ø¥ëƒü!ñÚÒh8Ï3Ã™!-=y²Ï×£'d/§–y4$zY0òG¢v·ÇˆÛ¯í}ÆÁ>yôÊŒ÷5ŽøÑ#T{Î–!ã‹úêJFðùÙ_h‰‚NRV]è£,~!Ùï%—,¾óE!ÅW\ä<Ÿš/ßÑT™ob–Ð2ÕµÎ¿ˆÜ|Áó“\Ó<r×IMÈ2c4öJ0ÃeÒ~¯
ñ„GpÌ”åpndŒWPñA”2b$¦š*¦‰Hˆž1Cœ÷
,¦L„XgÊXéÞ”Ö”o™Ò<§”XkO0×f{j*§ì(&­ôàlJHDó˜ƒ™r—È™ü—Ä²0oÍA"¿a2P"åHÆ&"‘ä8s1ãÑÌIQ3Q¦1;bü†>\¼J$‘"3˜ýúÛÛ æá© …Ifeò†ƒßÀ©eA@["Ëí4".!JI§pØŒ‚aÉÀ\xˆËJ B.4™Xr-üQ á··ïÎR>g¤ L%.8+Uï30T³L¶Yƒ­áX¡”
ìð§zqE¬”óÂE9”Œ«¸C·‰p©¼gœg\Å% AgmŒ›ª“£Dó§Ÿ¿<Û'ãV	¥X3|}œÙ¼Í˜[2e§jU…ZVb–Òå•Êi1@»Žà$`¼¢ˆ•
@ÄÌá–Op
ðR“H2;gý±ž°DxÆÙìqV*BÍd nP¥QÂ•–|:er@Ø- rFFog?7£74'Osa†Ö"6•)¦•RªÖÊ)&]HÖm` ’eB3ë_Ã@–QwÂô‚±Ô«5´D¿¹«¼‹—SÄõ8¯án1½ŠŠòôè»q‚î@Ú˜,U-Ø Å®
 \N>£Î_$¾LS±¨¢h1…ø“	,Dž::Ú«KÑ¼#òi'Q§:„*3×ƒ¶DÒÂÔÏ‡/ðƒñÙDÓá€âëÁÌXÖƒy80Á¼B.­›Ž,>G˜;ƒ‹¸mpe&ÄâÍß3ñ\³)ý þ2™³´Ší74-mB³ËÐe<¥*!’/ñãóñ‹—6!‡þ1'4  @s6%°Fq]šP?$,WåJº & p’Ù©‰æ„ëpv3×ÀZ¤âDigJ;ÑÄ§¤`ðI®yÊ¼I€®¤"‡Q$¸*¨!½jTG)`ß·çÛØ÷{)4íÉ×6¾ `)p~ ‚
Ê =yað‰@¾GšødSžo0‚UXrÁhÍ9t¢DZ‚Ë_„•:(tG` « ’f² yˆÝÒ¬HÙIôÏË[™3ÔœCÔ£9xEžòŒk¼6¦!û!áå]ì‰¨G^f ?ã‘ŠƒÆ*4A ŽQp<°ñˆ|;þæ{Ì¥¬,ø…àhCeuÁc^4ŽS¨…°Ç†œ7DFÊ[é›gÔ+!²+PXK‘öäßHþ–‘÷’$)äôOÇ8ž?Ã ¨!\¢~seß#wÞ—¦ñäæØ9OÓFòù4¡Ž¯—''éÏ ß§j®¬|ª5Ë
¤%U™Ù•ö&$Ã•—f*÷1ßó,c1‡a§N‘˜LlŽðþýÏ «¤Øðû=Ø+§ùyÌÇÀdK7˜ ÕFÀ¼fªœ¨¥‚a¼2ÕµÌ0[¶Œœê|"d-0ý|Çs÷ìîÊnÅÿ`jÿ`&}ÕfÖ¿“W>åy€ûd©™zæHTê³÷ÉYÃöÃ\Í2@Ïˆ*¶ü0}(¨BJ…Ü0hÛ)l^[Ø¡´(xÂzŠt¥È}ð@GImU7ab,»3m‡¸g¼&/7¡vÓ;õƒpê¿ d‡öl#©¾²Ë¾lH„£–V½Ðÿ=±nÂˆ€UÎÖ0Iç\\-ŸÎzzí% ü²à9ùñ=1¥&—¶Ö‘!˜Uð|lRÞs hä*Ø€Õ`Í÷ß-“ó®_=QšÊÓCt¼Î=´±ïCËTâS,ëà°à‹KÛÄ5æƒÀ^u·!äS¾í›@2‰mß	æ¡/Ûê/ƒ±jÌf#Óx¶kVäk²´~ù¡ÝòÃ¥ëÃ%Æb#r)'\KŠÐa«<¢Øœ¯Vl8´H8M~°èÎå^+³lñA@uQÉåy>40ÇÜ;¬`ªE…F1ŽËYöúBo‡r™æPhìDË§È${DXùå7{™!ŠÁsJ5Àô"ò³˜ùGš?§°9Î‚þŒù!›`âÚKåìØÍ¨Ï³–²ÛNÌÛÍ;%œóŸœe«oÔÂõ%¤g‹Ü•Å5H.`›Ü
à2ËÒªä‡ÿyÆáF	Îó¹‚BÛ¯}c¤°ËÖ ³»Ei¹YlàCûk7ŽuÛlEÿF‚?}kÈUÜðZCë.“7@98Õ¬ÔHpuçG­|¼ä„Ó]*ŒËÖ èú"èõõÈuù½ýáÝå§Ÿ>53‘6Výê}^ÇQ¨ïêb&@_â‹<EiQ¸ø¯L ˜ÿ³Ñ¨’L—F[FXS‚žQ_\«2'4ueSIÎùÞæ4co¼V×v)B˜"óÅÅµáÈ#Wê¹±–™µ¸’™¼°\+R_>S+b»f×µUìwa¶M^ßÛâm¢@cÑÓëé§0½ªæ–¶fsƒYx˜ùW&bbjS»H:Ð i€ýÞA‚ÂÀ#Ç_ðÎUÏmÔ¾Xå–Å’IYåp	å©©áïŠKL±9j7Ø½ý¤XR¦ÆÐ5¥Í¶¢¸Ì
’ð”ÙnlHÚ¤Îà=ÉÜ&¢-°­Ëº Åx²<9¢>L.^Úncj8±Ÿ›©ÁôVFiå¶Q3ys/œD´äN|¨ÆÔÜË¨cúFª€{×L2½‚3j@]‰‹ÝW`Ö<9ŒÅLë„êeÁ# /g4Ó4âvÃÕ X¿ÊE~æ¶ÕAœœƒ%­	Ì$8Äµ9™(smË"Œ½è	nœÀóÊ KÍ^¦ç–Ñˆ,¨4Ô¸ o¡vÑ«C´—®ÊùÂ©ªvÇ™¼}¶öŠ¡'ç0r(æ5€½‰Ñf}yÎ‹fÞ¸Uƒ4­ÌCü«JVþí(Ò0$-EÊ1k€ÔY–‘é‚Á0ÍÊyn#o3ÐfTø”7äTYÂœ±×E±¬¶®ûüŠj[öÖzoÞåÙQÏªã°*°ú]ödØ…á56sÀÆ¦“ƒÿaÎÂsî‚Š/Þ]º7£X©IØ&3˜ÔcÜwS%ÃäîƒÇÒ¿ªihs¼›)¤<Õ¨nI·3’_T©41¥$îÎºLÓû¤¹ed¥Õ›óŸ†(wE˜TôÎv0ÛFp'o™þXÑ¢gé!Xj»ö'„n¯•ÍÒpÇ»]­ÕqÃ	qHµ5Ë Ô8Çæ˜µXe·Cµ§ªº='fÂ,K»N8”J9ØÃÅæzÛ3'vuÊ3Ý¡~w	ÖÌ2B˜áG’Û}DöW,ü™Aß¦h5"?H)$`gSj\c’²,tÂ\vÒÀcsÐ£×=ósC€·‚Çô êº=8ÞjÆ¶r;¸]§ëqÛÝåö[‹íîvš»«¹e‰]ÜÌïoØæb=V»¹×®PU´q+qzåÇƒòªjaï«J­|ªj'—Ú	'Àf &¸1¬rÕ—fÇ-ÍÖ·‘¶8öUÚQ«´8vªÓð÷(§`kçŒ_åŠ=„G©ÝZºá×8a_¶Œkç|-Šƒ&Ô=l­èZ:\'ØúâîÀÅ][Wk_5ôuÞë¼Ö~¶³ ½r<½ö±ÅŒk¦
±3ÅðÞ¸Íÿœo%Áí À1cvÅ–FLÜ]åÂò€Kªrá²d~¨‹wÛR«‹ñßI¿yúÙÏüf8ž™{˜¿í®V‘qmî’1(‹¡Ò¸]­,þ¾2Mi:¸µ\EÚï•V«[S*J ‰¶î{ëùsPþœ{:qÇä=oÊcãVœYÃ”@\ÎÜË”;²üÛ€ù"Šž.¦‹(:[lœ˜ i]æÑ•¿Ûñi±å!•¡+®tßTTËi³NÔC·‡*¢¶øŽkFm¡ó0Ç²¯þvu9°áŽK²^JKwëAÛÙÙvÃ,D¢¥£áo¾íåzÜvq¶ÚŽþõµ3œ“ÔÖéz ÷âx»ã·ŠH{„ÿzøv÷?kÆ=¸Þì¿½÷õèíÃùvo–®W±½‹oÞ.®W™qW×³‚Úº^Þ>\ogðVðè²,Û—èGê®´\¢mY¤¯,ö(§ÑÒvÑ¶3Š}ÏåH=—n¸µ€¿ô¿Ã¶_º8`üúNÌ‘;1Ü°U)ü=–ÇiÊtqÉŽXöý™£ögº9åÖÿ~ü{ Þªéè‘€ì»6GíÚtpÈn•ßÀ9j§‹C¶²®/û­T‡ÝJUúaoÙ¼‡›öà¹ê¨'ÏáÉfþê½x¸ŽÔé²o•ÛˆcTÏÃsÇZú"Ï
%¶1h"÷:<Œ¡O€?MBl£-Yzþž?ÖÒ'@ J´`¶¡z
…BhêÓàPƒ›HÍX\žàwU7›ÛÝÎVop‹fIÐëêž‡fÍ6‚ãÃj¹Tò¹¹EñóXDÏ±ŒG’ê±¦þ<¨é-\ aŸ‹fÁóß¾&ŸÇãñÙøþýþoçøH¦ÇCòxüÂ|úW÷©.àeþøËÆ ‘ÑÛ«˜¥ô(Ï¨ ÛÌç/_ôÙ†qio®ßàÀ§oR¬HÆóRÛ'_àC$ðé?øÐi´$¡¨œñhf‘ÅÞ£{FRó1¥«w46÷¹ö·CÆ=àÃ–È‡ê±ZøÈF0¼evã™{KBhQ¤<r<tw:NÙK	JÆ2–»û({:ÙÇ­b„X·æ…WõOØ´ñFoY3X¹•ó IªAxªÏð1ªˆÜ£ÿ\ÉÉ"R“    0707010005c866000081b40000000000000000000000015ab2175400000ec6000000660000000000000000000000000000004100000000root/usr/share/doc/opensvc/template.service.sync.zfssnap.conf.gz  ‹      í]mÛ6þž_A$(œà¼Š¤êfƒî] ¸¶9 ÉÝ‡ Ø¥%Ê&,‘*)y×Åýø›!)‰òîÚRü’õAþ°/z’ó<3œÒÒ“'ûü<zBöøAqz%Â!ÉW#ÅZší$n·_Ý=úŒƒ}2úòè'3ÞórÄa×lu-U4©{ hÊàÄÙ?¨"ÊŒNVµôQØQìÏ‚+VwáM´9“)¹äšKÁÅlÒ8±˜I^÷ú)Ì	.æLñœŠÐ5”0“7dÎhTö‚0Sö¼ÎXÈcÂ53&àÞÐ¨ZÐ!\qaÔbÃ¤ˆX|ÎêOÏenÏæ’Ð¥ä%g"‡“PŠ8áa®=vLY~Í˜ )ôœg‰¡^-I1-2@£q‘$kp_T,UJó:D5y­‹éR&o‚×xá› ï^G4g9ÇÿÉÛšB{O œ¥ôf‘¬ìõ/FãÎF/ÏF?ãÑdôãäû ˆŸLÓçÄ^yc¥ùòôhS
Ûm¢¾dÊ“?•$‰¥>°“CÓøÛ8ŸJƒÉÊðáÑ,×bP L‡ïÁÝôMA(O|+Ó½ƒ„ë|Í¦¯ç<g:£!X2Ë¨¢hIx‘q¥û4Aƒƒ;(èe8Žõs<PþÈÙ}¸,ËNÎ2^.r6cªÊG }JoxZ¤Dé”)£À î•ra°@/ïÓy6»³BŸœÚ7C•G,c"ÒµàP1`ùe6Ã)ì3öùKÓ(’D^ƒ5¨%«€ÃLd”>å""R8ÇäZsAõäð“PÅ¼©¥¾æ&kTŠ(*fŒLÈh8¾À£33¿8œ J<p?˜)K{0&¨Wª–¿{ÇæÎà"n[Á\ê9ˆï!ÞqKWúO¦KÊÎ’%M
ŒI¡Y4ÄÉ/œÃßM€px<zñŠXýäWA¨G €æl*`æyA1¶&t\0r‹$D@¬
màž¥‚ðF lÚ@¯R1Þµo`\?A|B2G „†èØ©èJ*r”Š¥%tC•ý°SƒRÀ¾ïÇÛØ÷g!sÚ“¯­AÀR`ü@
:ôT6|BaÝyí‚fJá1ùÎ(X¹ ŒÖÜC§Z&È°üÅh‚/—€5z²0˜K!SyŽˆÙÌgˆ$ú‹â†„æ=$gcðzT€Uˆ„§BÛxî³?“Àz¼×úboÄ~Ô‘JÊC%5´¯O£`x 7.£€|?úîg¸ÆÉ‚ÿP@jÛ¼æEã
OZ~-íµ>ç‘‘òVúæõRÊô:œ+™ôäßHþ–ž2÷„ÎÈÓÎ‚ãgèsp—¨‡"®íßÈ÷…É7Ü»àI‚Ò*ùãêø
~9vr"þlH Yh+if’Jè¤.Rë=Qnz7.Z^™©¼ô¡ø7OSqvâ:‘©Þ¿ÿÝd;i=6œqÿ£—ƒÛÊyÔÇ@e+7˜ UŸÀñÛžébªW†ñŽÂi	f˜­ [FÆAOø¬¦·pÜwÏî®ìÖü/Ö ö[3éë6³þ­¸ò)~lÉ÷3G¢"?{Ÿ5l údV|ÖeKn!äÝkÎÃ‡Œj¤”Ïƒ¶òGæ³…úšf ¬§HWŠÜå ±¸EI­U7a¢/»5mû¸ywœ“W›P[öFý ŒúoÙ¡-ÛHª[vÑWiç”‰pÔÊvÏ·ÿ’XKß#`–³Õ%L“——×ŒÏæ=½öâþQð‚üú†˜PK[íäßæ¤ZLÈ;€—Áz¬¨kÎ;|}´LÌ{ÿ…Î©:=DGß¢Kg˜Ö]ƒÁ‚-®lÝÕ¨{¹HE Žä)ø‰)‹ÑCÄLÂpÈ”†ß–mö—ÂXsŒfC¬¸5+òœÜZNg§7)CŠúôó—g‡ÄÍ,ñ4rŽrå55å¹¢üV ³ÄÚâaÝ¡EÂu!—dÕ2$0 Ÿã\^ö
¤äƒÄõÎR.1Ø0Ð.Z)0o?ƒ™²9]r×HÆÉ@HÛþÀ.›j0|Hr¬„àËgÈ${…Ÿ)™zÍQÞSè†a9‹™C8Rñœª™ÞžWÈÜÃ6Ì@¾ñÚÙ·óù>›`âÙKeìXÍ¨ï³š2ªµ‚rc)ïS¬3þ“Óluf_v³ÇEùrbÅÞ’ÿÖí´Z<¾€ðìZ”JeQ’sØ&¶¸°dPñÃoû~¸‘‚s±Ðh=—EO‘™‰¸Á 4v	§¬œXdPºös7Žû–¦mFÿF‚o¾Uä:î€X­!ºuÉ œz^äHuç×\—þ’ƒN®éJƒàL"¼äê
<èÕU`“:ˆü~yûîâÓo‡†×s™4Vý\› ¨ìcà÷÷Vv1—Ð_âù¢r™9ÿ¯M Yùo£P¥X^(mbN	ýœ„i4¹ÒEäOhz¦3EÎpŸÌ24;dÊ^]Ù¥0³Ø?Y\O®Üƒò<ŒÀ_u¨çÆzXfÖâvHfòÂ:rÝ‘ºyONÝ[5»ªµbÜ†ÙyËÂØk“*‹ž^M×…©P½°´5õÌšÀÃtÈÿ’0a"&&7µ‹¤ÜÛ3Àzï Faà‘ã/Xçºå6r_Ìr‹l‹É´¨b¸˜òÄäð·Å%&ƒÈÜŠ	5:‡%Vo?i‰QtMé„/€wEš‘˜'ÌVc×¶œ•œábI2·)il÷E]€b4]Q&/¬·>ÕŸ¿‡Xˆfj°
‡~kÓië`IDÌäÍ=†p3Ñ’;.]5†æ¥ŒÆŽÅF¨0e6KYÇ{@] ‰	‹ÝW`ÖJr™ÒÍWb¼œyÒLÑWe`ulìJHqf–|#tŸÐ¤UA*ÁCÃ%®läÉIe!r›¡ïEãˆqãÞW€,YaÏbœ[‚€\Se¨1!¿@î’¯Ñ6]”óÄuÏïÚ-c*õ³Å±W=9ƒÙ÷FË½Ø‹Û‰i|´Y_^ð¬÷nÕ c+„Aù©‚•ÿ8Š4TAK–pŒ tVEhª`0L³r.¬çm:Ú”æÆ>‰†œ*J0»ß(2[™¼ß¯ÞâÆ^S«ú½y×hsD=«ŽÃ*Oë·Ùã‘aømlæ€õM'ÿÃœ…þËýÝ;:•2ywáÞœb¦¦``+˜Ì`RpßL”dƒ»%–å§š‘†6Æ[Î |äIŽýøpñ‡›‘ÊE•ª'&•ÄÝYIr—4·L€¬´³zsþ³Aq¥â®ŠÞÚfËîæ-Ó!=½Ô'ÁR[=Àz®!t{­l”†;.ØÍz®ŽNx„[@ª­Y¥Æ=6Æ¬Åb¥¶Ð@e%ªº='fÂ,‘+ïK7ÎÁ¡TÊA6)6ímœØå)Ït‡û~S(ÓFø¡âv»a!nwsË;÷µÉ[ä­RRv6¤Æ5&¥Š,÷\˜‹Nxl.!gRç=zÝÑ£±]ß;xkxlA î¡Ûƒá­GŒ~)·ƒÙu1º·ÝMn°5°Ønn§¹Ûá™š[–ØÅÌÊýÛL¬Çj7óÚª
ƒ6f%O/ýxPVU-ì}µQÉ¬•Mõ@ídR;áT"°¨)n­\ö©ÙqS³{»‘¶8öYÚQ³´8vÊÓð÷('akgŒ_eŠ=„GÉÝZšá×aŸ6kg|-’ƒ&Ô=lÍèZ\'ØúäîÀÉ][SkŸ5ôyÞó¼Öv¶3/½t<½/ö±úAdg·D†’àv à˜2»bKÃ&ª¢ùuzJž¦UºpQ2_Ô¿Éð–ë‹ñ¯Þ<}mö3¿AðÌ<sÀüowµÊ”çæ)ƒ"ê·«Ù |®LSšùžŽ{ 4ÈÂ¼Ü+­×·¦T” mÝ÷Öóç ü9ötâŽ‰zÞ”7FÇ­8sS<q]8s'SnÉ*wØz4ØÈ™õt90]döÐÙâ‘`ãÄAëJ„—f¿ÓÉ±å!¥¾+®tßTTËi³NÔC·‡,¢ÖøŽkFm¡+aŽTŸýíjr Ã—dK)-Í­mgcÛ3‰–††ßù¶O”ëqÛÅØj=–Ÿ¯áœ¤¶F×¸ÃÛ¿uDÚ üêáÛÝþ¬÷`~ð«ƒõõèíÃøvo–¦Wd‘}ŠoÞ.¦W©qWÓ³‚Úš^Þ>LogðÖðè²,Û§èGª®´\¢m™¤¯-ö(§ÐÒvÑ¶3Š}ÍåH5—f¸5¿ô¿Ã–_º`üúJÌ‘+1Ì°U*ü=–Ç)Êt1ÉŽXöõ™£ÖgºåÖÿnü{ ^ªéh‘€ì«6G­Út0Èn™_À9j§‹A¶²Î/û­T‡ÝJU)úaoÙ¼ƒ›öà¹ì¨'ÏáÉjþê½xž¸ŽÔé²oÛˆcTÏÃsÇjú<Ï%¶1hª]ô:<Œ¢O€?MBl£MYzþž?VÓ'@ 5J´`–¡z
…B¨êÓàPƒ›HÎYTœàw•7›ÇÝÎ×p‹j+è×_Ô½Íª-€ëýl¹Ðê¹yDñóH†Ï1	€#qõZÓò>˜Óh f
ß‹fÁ+Ïž“ÏG£Éht6ÃÏŸã+™ÉãÑsô¥;š@ðB<þ²ÑI¤ôæ2b	=Êë3*ÈöóøÕ«ƒ¾Û0*ìÃõø$ð!Åš¤\¹}ó¾Dßþƒ/FM:•øŠÊ9çY¬=ºw$5_SºþDcóœëòqÈø¢|ÙùP½V_yÎÆ‚Ìn¼soRÍ²„‡Ž‡îIÇ	[²„Ð™b,eÂ=G¹¤“}ÝŠ'&Fh€u+ïe^Øjù†Mëï`ô–5ƒµG9š¤ÔŽ§:†¯QEäýÈ¾Fïà    0707010005c868000081b40000000000000000000000015ab2175400000682000000660000000000000000000000000000003c00000000root/usr/share/doc/opensvc/template.stonith.callout.conf.gz   ‹      íZKoÛ8¾çW0ÈA-àª=·M »öRH{(Š€–Fa‰äòáÔ‹ýñ;J²œÄ±Ûi¼/qDq43ß7/™‡üœ]°~‚8ë”®œ0·ÔÀ2^UÊ»§‹;¬v‡õÝÙ÷ÆØ‹w?Î>½—­ÅggAû9,o•Éß¯”Èê¯¿9à'8ÉfJóiÝƒ¾aÁÀß^Èï-h£Â
T_Îhñ3¯,­äPp_¹•Îo§B¾-Úe!K0Âq™5O«€ì+ç­*ÀÐh0qÝjÈD!2¼g÷fä|ŽÍðŽo%²DÕ5—9sŠÁOÈ¼C’9nfàÂÅÆÍ)nü€D/÷tÚàh¸_êìÎ§;xûÝÁœŒdSr¦'\H30÷ à¸É±[QUÌ™epyã>ÆÃ7åM5X6…B`èý
ÍaSžÍ{Š{J¬bÊ0ž94›kE^¢@s|fO1îÕ÷¯‰>b³+V	ë˜*Èc)»2SáÐáÿ˜ƒ$¢Ä¼Š©JÔÂ5H4*8Õƒ¬EØ"\‰wZ¡´”]«Vr…,*h Áœ±|êdM¡äâRöá‡Ÿ¼ÖU¼7Á]\'AãÖŠ™´t½#X¤NOm°¾â±`"ƒ	K¤Š¶$Œ/”È-ãr‰9ïbYÉå,°2ÞÑ“UU÷T&1a·	C>ëÃj¼T‘o9Ñ–,¿ìPÞÀ\ë§Ò‰q÷‹’›³úSÒù{×§¢§Èµñ+:·Pj“c›Drržý|Ð‚ÉBìå"GŽÛ÷+mÙ¿«çÜKS¥ðQòNâÈÕ­lŠQÕÔ$1Ãú€pI•c0x‘	ü+Š~Nï'ûRÈ¹e˜]l©|•SÖÑŒjyŸ!qHåÂrS '´eâ²±c	raRÿ3ü
ô£#ïâŽ¸`Ô!B‰ˆ™–Pœ¶ô.P‡Pk¤lCMéƒyur°‘s±(`©²ó	³K™E'´³àÎá_fà Î‚cÆÚˆ­ZâPR‚°„zæ;ª6À}—
ýø6)áõRLØÔwFÁEå5wÅU
³KN[5v mhPÊþ´PøŠ<¾êE*1ÇbíkÍ
Q]ZµíôU{A¤lÙ†¬Ü¿ÜåÕ-_Új!ŽNÞáƒ°÷ûc’2‹¬ÌŽ_Ó4}œ¡CÝÎL’‘`7ÖÊ‹4¢»k“p‘÷™Ô´¿´Œ-*¸Ø¾Å}ÈÂÛRdåz¶AáÖ®'Ìë”}5¢ý³·8…[éˆ9¥A§ÿÃ‰Û2‹Ð_ªPÐä[¢yO¦Æ9*ôv§.ôŒ¡D’BñNZgçç,Ç>íùy nG6ä.¢·’N2Ÿ¾¸Dyß-PB¤—s¡×	ƒxtFRþð²A—*b*MÙ_ÜDgô]Ãf(ÈŒñxï%k0<&àæÖ³Z+rF9mõø˜Ô6N-O‹/³‚6Ä ½àEÛ=7å±ä!4l‰Å$6äØt‰=·†P¯ï¦”®$NbM\Ì°ÜŠÊ=®¯¾Ä”Ãjo©¿î4¡^.‹µ±I>[ú5màfÝ”Ó¢Ó£Ãì¯(¯º”X…lf„výW•Íû°5Š<Æˆ”ýnŒ2˜Eá¥œ1>íhÒDÿÏÉZY7¢·;z¼@ï¼;xlA¡¡;@àõs‹E§æa·KÐ¸ír‡€m‹íávš?½ Pk~ Ú'ÌÚ„¶…ØˆÕ~áµ/TCÂJé©}¢Jé}ƒ
%‰©¨½Bj/œZjZ©lŽ^¹G³çÍ6·	‘¡8ŽSÚ³NipÜiN[ƒDñy¶aÁø¤P!|–Ùm`>%ÇÑà¸cÜ°à0¬C=ÂvÔ‰n`ÀíÛ8Üy¸jÃ§†qÎ;òœ78Î¶aÖë@ožÞIèã v[
VóÞXÐÜÐiàö H¦d.âï¬ˆcñ·[že€˜/ƒÖýñ€%=O'Ý¸pµÀOŒ4: ÄûGŽ>‘zõ‘Žš¤iú:eñèq<Ï¢jáP½	>FO¬Ë§KæuDú{Òè0rtax dtÊ™d…¢’hëñ‘?GåÏ‰°g'îP0òæ¨¼!âÌ¦ôÄíÂ™™rOV<&²FƒGù¢ôH—#ÓEé—Î–	ˆ,ÿK‘?]œ:    0707010005c869000081b40000000000000000000000015ab21754000006a3000000660000000000000000000000000000003800000000root/usr/share/doc/opensvc/template.stonith.ilo.conf.gz   ‹      í›MoÛ8†ïùrp¸ÚžÛ¦@€n½ÒÅŠ" ©‘EX"¹üpêÅþø!%YNâØŽí4^È—º&5"ù¼óe+‡|]°¾ÈœóZI_Ž™_`²Òû™;àÍöìÎ¾7›½xûãì}Üï%íøìŒV>ƒÅ­¶ù»å<·Sð8ôæ€/:#'´á“
º{}³hÀÂßAZÈï«çÒI\½šÆÁÏ¼rq$‡‚‡Ê/—ýE«8 U	Vz®DsŸ
xÁ>²xÞ.îlw„,¤À9SPx­ˆç‚wpg|+9°s°¬æŠO¡å™ÐÊé
˜×Ìpç˜§Yé”q¬Æ™9Žw=qàš¥‚—çm-8£UŽ»c<ø2C³ÞMxÜ2óÒWábÞ'"H­Ðk©¡)œök±utvàöö`ÐQAN=ãRy˜‚½1"Ä[YUÌÛQlŽqz§ƒÀˆ@ŽM ÐXÁ«Š@M¸˜õ âµ³&öÚ2ÙEj­ÉKÜãZW›º“#æÀ¿úþãõ1¹9Š?=fW¬’Î3]ÄËØ•Ho9¡Ãÿ3ÁRbÁyÎ­¥oH4Kðº‡¬%ìPè¯|¹*´–±k]ÃÒ®TE 71çVÎIO­	”|.Ñ\Æ>#~øÉkS¥¹#¼Š›™Â8àäT¥8Ñ	,I§g,^àBÅ=n„‚Ž0f#¥Ó^FŒÏµÌãjÑg1Qr5á#ÎèÙ*¬®{KŽfèšàFFâîSÐ¢h)ê7ÞÜùeGyr]˜¸_œ$ž¢ÝG³ÄSÒÃU{kOSé¤âÑ¦·Çñ&œÜÉv#‡Š	˜BeŽwï–«eÿ.ïs/hL4fd®îŽ\ßªöPÑ«:HMð—SÌˆKé!H!ñ_Yôcz?Ø—RÍÃèâJª<FÃb¶ï+€‡Òž†›, yT@›&.›}¬A.‘úŸ‰àWÐOy—;rA¯‚ ‘"-‹ œ®ž¤©5V6QÓ†b0¯N[<\L
˜ªÜlÌÜB‰tm“xåõð¯#0Á™sÌÓ˜±Ty´4Â°QAÆFX1Äüè÷])ôýÛJ”D0D1f“Ð—U°±˜¸k®Ò]òx)u	¤6ÜPÆþtP„*žø²©ä“u¨+dná<Ôýî¡X–Q”­ÚP•»ð[£]^Ýò…»‰%ÄÑÅÛ>ˆz¿ÿ8¦()18TenMz›eÙƒâ¤
u³2£Vc­½$ÓdºûlLò¾’šò7c‰
>•oé:Tám)E¹š„¤k¨"nã{Æ‚ÉØW+kªŸƒÃ^ˆ¦’è¢rJ‹‡þÚ¶ @Î©¾Ô”ÐÔWâ.ò~O+Ýô	«SO5#¥È¸ 43Ž³ós–cŠ‰öüœ¤Û‰µ‹ôÖH²“ÀIÆÓ(¯	Žf1 Ò7“fU0È£ÛdŒAõt¡"…ÒŒýÅm:ŒþÑ0‰J’˜±ð±çÎÑ²ËS ¦0·ÕjQÓÓ–·OAmm÷ÑÊð´tñ23h#ŒxítÑVÏMz,9…!‹[`r…¹6Y`Ím€’áõÝÒ¥ÄqÊ‰ó)¦[YyZÇõÕ—rX\¬¯»•ÄZN¤ÜØŸõš±p³º•Ó’Ó±¾ò¼ê¾ÂÄ,ä„•&
~‚ÚïÃV$ò˜"2ö»µÚbDÐ±)¢/å¬d´“Iãý+<ï“v~ ·;=^àéÞè!êÝ¯˜[Ý2wp»]œnà¶¿ËÛ
‹Íîvš?½ Wk~ ÚÇÍÚ„6¹ØÀj?÷ÚUÇ`·Òf µWi³¯S¡…m|j µ—KíÅ©%ð8¨I¥ÅOåfhÍž·5[n‘m9]Ú³vi[pÜ©O[Á?P|ž†m;g|’+Ÿ¥wÛÒŸâ„CkpÜ6n;çÛ¢9XE=`;jG·¥Ãí„mhîŽÜÜmëjÛwCŸwä>ok?ÛÄ¬WÞ4'xzOB‡Øm)=8Ã¼q`¸O·€úsô;+r¬!ývË… dbƒ¢U÷Û6êô¨k®–†èáø‰žPZùƒ…Væ_}ˆgYö:céÑãô<‹®¥Çåñ6fì|>Y°`Fd2Ü³FNGH7ŸrŽ¶¢„’Šhãã!ƒ~ŽªŸQÏNÚ‰uÀ ›£ê&žñVšY£”ž¹]4ó RîÙJ‰¬ÈàQ½h3ÈåÈrÑæ¥«¥'‚(–ÿ Å"òÝå:   0707010005c86a000081b40000000000000000000000015ab21754000000f9000000660000000000000000000000000000003900000000root/usr/share/doc/opensvc/template.svc.prov.zfs.conf.gz  ‹      ?o1Åw
$æ»Ô•¡R[–Ží„P]r(âGvˆÔC÷ÝÃèŸ¡[òžýü³·OÏ/ï¯o;0Ø,)£“VÆÑl=;u<_Ö>¤ñfZAiÚsñ«Š’ÞÛÊË–ÖCÇ [ãø0_ì }D[ªzÄëåÝ0åæÄ¹ÚÛ±‘Ê°Ä[m "'u‘0;vŠJ^X¾÷}Íx¸Î"bb
öµ.›E¸Õ!Í²Ö­µ0—ÿ€;¯SÄOy‚^n.y«ä•ÃþNñ>«Å´–î.8¤cPÎ—sK‚¹>FøóŸlÃ     0707010005c86b000081b40000000000000000000000015ab21754000000ac000000660000000000000000000000000000003b00000000root/usr/share/doc/opensvc/template.svc.prov.zpool.conf.gz    ‹      A
Â0E÷sŠ‚{Û4ÅGðEDL„Á´&mÀ–ÞÝÄµÒEwóÿüÇg¦Vè»âÝÓêì˜"w4qÎû|tþÖ^=å1Y€Ã!îÕ	,¹îÒ·–É£CjƒÍBþêw‰ØZ"~K\n…ËXn…å
\}àÁˆé ¾çy¥}0d&ÎÆù¡ûX2%%’ZDÊEDÎ
^7?ª  0707010005c86d000041ed0000000000000000000000035ab2183a00000000000000660000000000000000000000000000001300000000root/usr/share/man    0707010005c86e000041ed0000000000000000000000025ab2183a00000000000000660000000000000000000000000000001800000000root/usr/share/man/man1   0707010005c86f000081b40000000000000000000000015ab2175400001ab8000000660000000000000000000000000000002500000000root/usr/share/man/man1/nodemgr.1.gz  ‹      ì]sÛ6Òþ_Ÿ3ïÜ¸íIªsi¯©ÇÊœ"+Ž[ËòHvÒ›$SC$$±¦H ­¸ssŸýÝ] $H)™ô¦¶¶‰ßØg‹ÅbÑ½zÁ.Æ'ÃÑé„=bÿ8|ôäMçð1ü÷]«;…¤þhØJÒP¬’uØ8Éôå€ñ…HòÅ0…­x¯àÒüv©ìôßãËéÙ´Õ}ÆL­×ìÍül|yu6¾˜¾™_²·l0ú'Tâd8LÎ(µu–°|)Ê£d.¹Êeä…mJ£Ö#ÅxÂÒLHžGÉ‚©{•‹äW9OÁÖQ¾Äµ®ëä8a—]ÙšVüž-S•—Y•wQ TY™Ò¹ßÌŸ™ñà2™.$_áÏ»(„ÜfËSø=™Gè1t!dâŠ~c€îË¼H‚<J(°äØµÆäºçvœzJÍdM[­7óÉ`|~>\'¾õO‡£áÅt¨ÕêN†­ªÐh§’ñàþÃ¶0_wõôƒÛ$]ÃD,°‹&•	)!{šTÍ³>&ê¼MkÈ>8Ú,¹Ùìžu:¼È—Pô‹PÌyç4	2Móa-	_‰/i&:œ%¨¦Ý½ºDˆØ²½þõÕ‹ñ¤…3-
É°Ö§¶^Šª#á)núŒ©ÔoUÕhéá¼Áì´úÌ|ÙVÝöÚä–Þ­5†nÂôC/ø{ô›`i‘gEÞe—©RÑ,ìŽÇ KqÔj}Å`pé[À'Åf\Á|áôæ÷€¡€óÄk~¯þs/Ô‘ù¦~HMÄÿI ú­J*;ŠY± Î	¤€°‹ `âP ÿ,U4Wd8v¤+åf1Ô;ƒ›§rÅóÞóñdÔ¿jM3Dó{ÆYÈsÎt"àsë8Ù/*MÚ,Pwæ-çZU¼lCåKg­é2]C€qñO¶‚áË Qþ!üÆBæªÄîI¤²˜¶”:½ëbÊÓjW´RJä[IE)(Ð©ƒÈlÈ~‰jY…§ã>Ð1XŠàö<§Ó=­ö‚VqT
•èÖ@+#ì<J;CÕbÎ±Ák&9gýË3­áÎÏX:Ÿ©ˆ.fGVMç,ãùRQw¸\¸šª6Ë¢WÏ¦ã32]±y£Òt6·ª”Q¤PyÒp’”¥Y~tÓé qÝ´=ƒy 
- h”Ôu"t¥"x\ ÖÓ»ž'­>¨˜#à¹¨
W•
{ßQ2‚f°´[5©‚‡8ö­M³‰øµˆàƒná(X…G7¤ï y¤XDP^Þheccp¥Š’®$æÐ>ÌNšÜ¯ÒB•Åf A×©{—ýéôÕxrò‰†y	$Í†ÑX,@\Ê{6+rÈ›W5;=åYÔP9lâ,YÄ
³0B ‘"ŒÁ¦SÀª^#´ò‰SäÔ
0IÑêÇ1°
ªtE‚c'%Z+üNCZ€Ðæ TÖ"^×30ž³ 4ø'®¦á!M@„]<?;uzsÖ1c20)Ä$–¤zuC&è²G!>øï×]Ø±$ê.è Ï8-ý¡»Kœ¹ÖóT«r€@œVt)[…ý¶Fà]§
ÅïDk
ÿk€ ZÂYÅ‰PV `ßL
¯{"ªÞML5j½W†÷@ØÃx€Ûs¾(eþ@â0'kL0·O5[yYnj‚>ô®ú§´÷ÂòX´"[-QCÃ¢¥p«÷&!…EÔÇÆî±FêC:%ÉÚåþ]cÅÕÊ#¥
.%Ï¸TÂkå{A`èj²e'LÔÕi,,¤]ô3XñÒ°)çg Y$_w:"	»Œ~0×j{²®ÍUKøf èÁi”±÷lxzvÑê3SÅ‘x‡„UZ÷;ø7üÓ:''l¹<Z­ ‡«(·]Í£•<YèÅËcx–Å‘ÀÆò@{ÃÐ}õüOlÉ£|G(§EES¦Âú¹óÑ5Q¶A¿àL‘¢«·mÏž€ª ¾bS£Ð`PåaüŽG1vÅòHeWÃýxÉíR,³ãŒ›<½ÑPQTÐ¡/Èxî´3,KÓ%½–°7¸ÁÏ€%×iX!Kñ4ª¶g(o<÷†Æö¤òýd.Ï2=¥?kJã»¡
Å±#ì1½Ë®aü–Œqt+n*‘­lš\.Y1[ïüìÇ!(z´”‘bÔ	Ðºìàoh´ÄJV°D`Éñ´*^Gqp	JÔÁÏe&4<Æbk®sÁCm”Y.a øÖ“‰€Á}h Ã»G³•×Cö iek:‡Qñ²pÔÐ1¬o”¨¡Ìäï†ÓiÿtH†&[L‹Bvãlb®›Æãy‚ƒ ù:•[ÌK&á#Nv‰¢‚KãÂ“5ÍPÛª*OÎ.û#š	¤ _šöJp,KDLD–Ê¼Aótöüâ( îÅ^ç÷™xzôöX3˜Èðô·ôº£oì¢¢«bø6¬Ê*#uS™Î°Â…ðkž‡öï„6–lÚ ¦ÎGS„„ÉÅÅŽõù­%šlØ›"ÀgÚä²™êX£È˜£>G›TöÎN*BÂ,Ð²Ž^ÀPÞvõWãŸÚ®%¶»Zü1É¯á{@(÷ y*r<E6ôa_8Ö:$þÒŸ?@‰‹ëDý.x²ý¹d#ÿÑåùYÿb0Üîç[¨ˆ.=@o¸£÷éO&‹XÅ~«3ˆI½¡&WiøÁÌeúž·HÙkÔ”¨Õ€“ ðcZÒÜ:•ât ýçÑ»ºÀ–ìÆ'×çÃé°šqÒÁ«šÑ‹õwù‡eŸ|©ß<Ö”`°àØ;…ÒÞ„Ašü’êËzß‰ª\°tÝ±LO&[ú!?Y/<k}b‰X1Lbµ­-7M{>cË€`¡Ú0¾ÔXÔMÍ‘›A·A,ÞÊ„„)Ñ#¨KOØÅ¶ð\NämGx½)ÈÚ–>´iEÚÐ0´
8R¸§L,ËÁVR52=’v…$BË‡¡T„áVt7ä^%“£„|]Ëšd‘Ôg¨cüH	Pm-’”Ð‘àœÚ–Ò×ˆÞmr·.î ½RÖÙ«´º…ytïÍ‚T[6ÚÜ+‰ßÁÍ¨û?Ÿô¯†­kòËf@}P–ÐHcW€v}Äóò°éOìÙn7l§é[ÙªÅŸ¨	u©éÆ6u¡ï& È6­ˆ.VxµÈ«E{ÌU"¹Ût5€h1t½öŽËÛUtÃ¸àÕ*£Ñzµò¿îòã1½+Lƒ¼}ŸzFú½×Îö€=¼vöÐxk-ùî%^Í»ß`=œS‰w,aÄž=#zFü£‘b4~êÞ…Ú•¢¡µ©*~Ž6uÙ-?Ø3bº18´·hé)ºIU”6lªooîoÙüoï¿àˆžšƒñÚv'`Ÿd™GêN‘*?§ò½(•Û1ª‚4#ÇRùûÐéím\’ÓÊ¦¥›x$£{‚Tê—Â}$dSÀx2~–d3/Ô{©hUSÍîÓº¦ŒÎ¦ƒmN)™¼÷›¶b¨è1ÞÇVÜ(ÉQ›òÎ\÷:ØgÂÐòbxõj<ù‘õ6NëYíXmn+ÕÍ^ñÜµ”BÚð_/ÓjñªjÄúXGnOÔ?Ÿ+Ç'Ã±$é))|ÉaRÉäIáÿfLÞËA×z
è³^¦h«0’´™/ÓštƒÂáB1cD-ò¥	ÇæYzW,3æ,MsçÊþÉÊOÑÜ^„ªŒ®ÖÛÀ;ž=xˆ§nR†«%ú•Œr:oˆS´ËStNd|Šb8ÎÓ"	KàäP=—a™£ô'‡¦‚uÇhØ˜‹-wwgÚh¢/ d…ZbnÈ<<Cñk!
±qƒîÄ~wÃâ›uåM_÷:¹Aƒ.…ÞRåö€ê@‹ŸqáaõàaÕˆ÷<¥H°4.Vø‚e¨]öE™G7›`¬a¤´q9.0¸ngŽÌ#@mgìØbu/ºÞ¤/Ã˜@™ðˆÛž”‚*ÕÂàRo´ µUä\.ð©›]áf.…ø­:â|NVÁq0a¡Syg¢;°‚á¦Êƒœn®p1Œ”©ÙªÑ™¶g}í7‘ÐbŒwsl{š/öYÿÕ] l\¬‹V¢wu6¶.ŠÕÈD|@éÇEÖ<ÊIâÃê>	ªSXš“H-Ë°Ãˆ¾Ç‡‡¶´C#¬¤õª¬	«Y‚ÈÄÐÓ¶6!WQBŒá¹ÉsS›×Î$/dÂËÔ²öÀðÀÿÆa¦æG,…¿å:RÂo?wFuèÿfð'h<mQé:?³×ƒðà>öžõ?žO[1á·òâ?=¾ãÞñ¿Kãb%°=d‘“‡T©$âtRsŒÂPxok‰:‚«*`Ïðš2üN>gBÎ
dw¤
=Å$‹ÌÄáŸg†o]T¦6ãÛ®1 ©„K©œE(ÃRi®ƒ áé‘è^D¡=ŒÍ*Í‰®ö!0×øÅCçAAG‚TÞÞU2ô’>•¦;H£UÆ[7ÕABïdøòl0œbÀ"ÊF¯±T®(ôEß7|RÏœlX>«6±˜7>ïäiŸfqrë‰H„¸À'òÐ%ÖÊ…@ÁiFU¾|gáóã|½ãÚ«Íx¨yˆwèõ“ Ñ‹U²I—BI_¡+·ÖK*bÇØµ„x~sŽ¯l56{<y<9xúðF×[o<^êxIÉ‚Bï‡eÎi¶ŽS#Ö&Å<Eõà% ^¾Ð‰<\TÀt4ª€‚.¸ÖýˆÞúÅ›6@$CÑã85t,g¼÷âYß|!9‚õÍ)&êšÍ@ZÜZå­Ë†z™:bß>úæñ“ÃÃ|óþ=Ä!F¿&ÝGßÿ¸sø¤›ÊEf4âÉÑá££~ûýì›Ù,üç“™‹-{zWýÉéÐ\ýý÷ôåððûïûäÑ“oŸ<±}	bz¿9JD7_äÜín‘ôÎ¯/œ&t«§H€«m—üØöyfØ3hu§¹èŽ¸¼­t·yjWe´SÓgT®8ÚL`²õ*z+[ÿ†¢Òú‰à‹Qì@Ws€&g-KkùyóÝž ¡éV{%âØ®nVó©‡©Ðå<	wHÂe‘‡é:qüùôg–²Ø_Mçs}ôäj¤òVñ*<Ž}-¸ÔuûŽ1å¯0wJËýçd¿5x†hçQœâü•/ÝRôDi¡@m†]zR>E«‡HÀä¼a)ð8{H8Ë—¼Ú®]Á[ÜI£øèw¬Ó·‡©Ðlån-¯5býÑ»ç¨ÂQEò>…þ:Yý*½×ævGÊñ&[EÃl!y¨£Ú›¸·\uÛN¤º~Ã•¶Ì89è.£DóòÑíZÃ»Rd)Ö{ƒ/±{õÐ#s2³ÛÅ	U`Ä7¼Ë‡¯¥`RÆƒ[ø¼”K~]æ÷0ô0Ü€á:O_i²jaÆ^ñ[Ñ'óþAOÃ…Âˆð Ï	éE‘÷ðäŽôi?ÌdÊÃ€+¼¦ FL6CïÙdÜ?ô§W­~ùÒb™Z5ÜÖ'™ÐåŒ£¶ÍÈ0Âfÿ¯—@{£þÀi¥6°-õæc 6îY[rZÁËa½ËñÄ~û£ê÷lói.	ÆÏÏN¯'}¼+¸íª aÏWùôÅÏ’¹†aäœãc<ôÑüû/‰zº~Ú»H¥Rîý¤‚%"C·Müiïy©%òt”gZ»^žœ+kDÚxZýÓ­‚		ûWØÍ*t¾º'†3”ëíáqµ#\-œ)§ÂÄ-äklC`ã(øM@#÷|Ô0rž¼× ‡eÍÙºŽsTÝÔ†lø¦Œ»Hê£ÔÍ”Ù¥—Ÿ=Xö¨­ƒ<éÓþPH1‡ÿ€Ì®¾FÅz—ýIÔê'uOã[Á^›Cª§Ý·ÇfOôãƒˆ©2ÙÄp 1ö©’*-0jd6¿„2Bð,d
HÀ‘ØsXHñ­Ën·û%¨ž&p¨
Âê¹óx=‘U†«²õºëéíº÷ã«ÐëÓT=}ûúõ1èàâÝÓ·oÓìé1Íóþú½¬wå w þÝþÒ_Z#|Ÿ1nÁÙ±·LŒ:í¹²9êÈ–ú=>”Q$­ªÜ>æ·Æ›³ ks’­´¨É².H>Pšæ<Vºq…·ð¼’%iÓ>Ñ_Ê1éÂÿ÷ÈÏeQ•Nš…1#Š%|¨ýï=üñ2÷3|l„¾>fô$`HõÒÏ×‡o«œ—°Å’[òzA»S7Ê-ú—½LQ-ä…”ÂäõªØ¾Ròc©èÉ¶'d“ba§dI²ñ,ç$úuŠY¢pËUtsú¤ÿE®Ô[a\_*&ŽûDâDC@·H7YgY¯yÙgú
H¥+ÙÞZ¯Ã™/n+y-Ö®MÖ­vÍ«ÃÖ+ÊˆóHb<NóWa]¾‡‰bCÔ”*íf	zÕtúj<9iõÝ	(»_¾PåÑoÙOl p#ºÊÜt€	$Cí0B=Œ]Ãå=›y}¬NO±Þõt8ùD=œ`¨rœVj¡Úñ$.önªCòªý¨Ú8iL:[P<m|ã÷U¶¸7«ìÌgÂ$IžÇÖ‘¡®QÒ¦C©Z^g÷:û¬³?Ð=-Áº÷²~­Ïn´	€oröEX3s¤­[œe5{ý“kÑ^	ë¯L·‡´äÑm4+;0•Ð:j?ÏÞ€µ@H{ª¾fBûQÛƒöjÉPÚ/X:b‚ÎÔlj«ôNô&ÃÑøåp³¯¡L³JÙì.žçÚ¡;µR_{g'ÃŸh.·u‚Õõ¥*4FÊ92C£Ìv|xMÚ î›â×¢Oí"\ÏçÅæYŒ¨ñk¬_c½]ìó“f6.BÓ 2 w15ch¿Hd#E&ÖÝ¢
¼aeGÎ'}P].Ø¨Ñ?Ž†WÛŽKï,Æ 0¨N±à•fÛú{³!N((¸Ùa›rè.!¤ªÊ{Jï0®c“–Û¢ó{Z~N´üv%ý~Hí…iÖÆµvmõ£¢”cüÿÓ›víÄ´m	39AŠKP=ŽõÏ§7ŸÇŸîjo:L†WÔ„3˜Ò
’úÃ)½aÐ˜Á¦ñ×Ã}§p‡µç®ŠarFAHªMŠ­…ÎâÒ°ËFöªús,æ¹É^ØÛÚãÝ[.Jï¡gbŽ!	°nàŒ6Szö(²m:M ­ÐzØxÑ¸s¬À4á¾Ã‰T¢÷!ÕŠæ‰´{"é»…Íp”´ô(ÚËÀ”¦ žé2m&`£
dn”xä©ºkªnÞuÎÓŒ,zÑ.E,=IšÂœÊ*ÌæùsŸ(Y¡Ó† ¥Ðuå­Ï%¾‡¼Í¾¯S:Qø9heg{W/&?»éÔÚFÍ•ƒê(¾î'[Í ì±n,T= whwËëúýÔr|Hõ Ú€Ò¬¾®yyèåág ç7óÉåõô;é_õÙÕ˜]½À[GççÃÁÕx²Íô 	¿•“Bƒ‚úyŒÊ6%C—µ´‹ã$øh}DM_Z =Wîy6¡>5cÄnÇÐhaw7’Æ{œ/±d«÷©t{º²k¬á¬ö\pÂVüï“Z;¡ž<¨-Ím¬ªqLB<ØÉ±Ó¾$Ž“Jvòwˆ^ u†íí3	f(êx¦?2µŽò`Ù8o>¯¶±˜Î~¶{ãg?€´Ÿ¢›…þR!ç4º¯œøãÌñ?0¹ÍÏ(|Zy§(×5A;i?ûŽeŒ!*=hwÚ@à£¼ÚáhÀ:¡ŽXå!ë!»kÈ†ªÃõº¤Rx¬z¬îV1út­ô‰T¶¡ï­!Ÿ!bÜ%ï¶¬¿?}ýòâ'/Ò¼HÛ7ÀÞ5´Å—lø²ï‘ê‘ºgHÅ7\ÞX~ñØ‹þÔÃÕÃuÏàºÄuñ4M‘Lƒt•á£êÃd„2J¤Ç°Çð~ax6àûâÄ‹ZÓ½ƒiö8s®ªX5?z´z´îZ£Ùª)VÏž˜—¬«ûˆUul‚uúràÑêÑºghM jUGëÅpÀÎ¦#VÖ½CkÎ³¬VúæÁêÁºo`UróPÐºNåíÿ·w.»mQÞû)f—v£n«zámì7J¤J)£ƒ Máí{.3€IÛ]«K–Ìm®ÿá&Ÿï§ŒÊ$m­N­N_[§’xN•J«.µgùžƒP­8^^#¯$-r6²â°âÈ0]eòZ¦Jkj¹º¥µŒ'ybÕ¡¯6Iê™S13jh6\,¾J¸ƒƒñ†IÔø ­'³Uî0Žý‘ÔX4´,Õƒ56Ûôeˆñ_‰ã:zé8˜4\´ØˆÓH©Ìäf"3ŒÇ5Wê–Tý®l:ÀIäi8P‹•ûÉ»ž-¹S‚ QîÆØ‹{ø8»³Ùˆ,{_|)68_´ó¬‰d6Š¨±6’ôlá^#0ûø7kêû»ËCn¼ýìFQÐ’²eMè1$2Œj{h_î^b6yà8™t“×°‚äÑÚg»?ß3Ã@Å¹t’£ƒa´îwë­nVáGoëêü!Ü…x(:lŽháCÎ##Ñ©/Šóv”pÂ€V2Áû˜29½GÂ rˆmlÒì`¦%`>@åÐ½9©p¶†²£&<Ê2Äšxƒ¯ƒOR!~"­Ÿ¿¾¾õ|ÁÛl8ÚGØ3Óm])¤:Uí®ÕZ¹Z¹ž\›®A‡àúqz³O;…àMÏ4Ê|à:Î$"ÖØN"}¸Œò®‘}Þþ÷tóåa¸û[oíŠåOƒ_"heC®•chÛX>õ ©(FSj¦fîc­ªy:t•Œ§Šq®¾3Eº±˜©—›òyp%üšjú¸0õÛÀ‡böÂLø›woiY[MŽa\i]~>\ù ¿JKüãÔ™`Ueœ%Œnø2}‡Åì‹d€¨ìæ³ŸççK	 0707010005c870000081b40000000000000000000000015ab2175400002732000000660000000000000000000000000000002400000000root/usr/share/man/man1/svcmgr.1.gz   ‹      í}isG–íwüŠìè˜€l´4Š3Á“Ì1@ZoÂVÉªPÍÚ¦RpO¿ß>÷ÞÌÚ@­y­"Ž#,¢ª²r¹yóžs²²²ö.~VÓ_O_OÔ3õ¯OŸýûïÃ§ÏéÿëíMVg£Óq/¿ñ¢E¦†ê<51¥Uzaâ¢Ÿ«Üd7grÄy¡cþé˜®Ft]yIDGþžd4ýÏ³ó7Óãioï'eóëý¦~ŸŸ¿¹8>?›þ>£Þ©ÃóÓÓÑÙ‘Üp4žNŽåjï8VÅÒÔÅñ<Óy‘•^Qff ×âÄ7*È•ŽU’šLA¼Pù*/LT×NÝÅ’StÚa/‡¡ñ÷ÔE•S¤Wj™äEônSmêßç?ÙæpÒ,Yd:â¿7O‰	rU$öd$ñ€NÇó`!uÏ‹$åuVPÍ|•™4<]˜MÆM¬ÂÄÓ¡ÔÓÚÖ™mÚëý>ŸžŸœŒ/Î'ŠÎ^OÇgT·^oo2îÕµ¥*P‹½"É”ö®éÿ‚*ÆÉö&”ÍÈ»Ž“[²ÈÂ°¹ìUe²Œ’»:¸Úí©_·É¶ÛéŽ+ÃMOCS_]­Ôp¨ËbIw?ñÍ\—a!É’¤ø7#Ö‘ùNš?²É(›½Õøâ»KuóÁèòâçóIÍNEšLñ}œQ˜,ÔíÒ45ñÜý®Þ|Uêž79ºRØtd ÞH¹3›²Ûœ›¢ÔY@~Pçš«7£Éé{r—é€Êp&:¨OR)îýuÖ7ÒÌäìªÿÛŸÞ½àã—¿½ÈMú²>|×çêdf¿×ªþŸúìçÜ­{c³ÐR=ëüI&I%›¾ëº[ÑîúŠœ£¹7wçè+r¢îÎCª°–‰VùÒ„¡Z„É{DÕ,î*¿©¦±ðòE’¾|q£ÃÒ¼lš¡T;I'óêRø/÷ûí3‹,)ÓµsœjïÅµY½Ü´sO'éæ²úJõ_ò?/ä§ü{ÐÎñÿõmðÈÌ‚ì¡¤=½Þøƒf'ÏmÇ|ïÇù`©¿ÿA§é^D&)‹—Ïúr-H÷(t~½oÿô½ùà…¥o|:>KŠ¦kËœ‚ó_e@Ý rò¼Ð¨ÿ*	hk½.QÅÜ°ÛÚþXO²§Ñ1'i<žbHQæÎ/F—ÓÞ¹øŠ¡Wí^´aÒ…™:xnà§Ææk}£ƒP_aP¬”Í^=))šùÉ-¹[Ñ¿{{{ß5#ÄãqA§°œ‘õŒWRñ…Œgë½4æ¨.…„8Š³£Å"cÿ¶IÿšsÂ²HË¢5öhD‡"(úQ„8äßÁÆ%ÛSo2ØR:ŽPB¼ð{E%ÙW:•«+S£ØªÔ6±¡VsšðV¯òÿ^Q7»ßÊsùÓÕ˜ÌŸýwL¹È¯æR]1_›ˆ:b¤æ¡^—ÁUPw¦½ü!ƒwOMMÁáòÎ
•%ÝcZíõÍU¹ dô2C'ûÓßH¢Uã*É¹SÊ”]DrãÔ*¤*†­<L|s0>û•z"M8a™’SxbŠšæz>É)äÝY» ý–ý„¯pŠV'†+‚4jŠæè—=/ƒÝp¿©áÐ6u„+§-Õ—j*ÊHê!Ü¸âžzÅ@Fž“P¾™«HË;Rö;.Ÿ@/ìM-ÚÚDâøµO·@WUé÷Ôþõíþ,ÒÞW§f|] ˜ë®Â 

“ã2º¢$óúv••q,l¤¼"@™çmÐÞê  ¡vm‡æÛÑñÅÉùá/ä/~e‘Öˆƒk2ÉÿÉû–}P‚¨Œç¡8äT¸Åá£ò±ÊÌ5€y×MñK¶ÅŽ´7]&·”ž°…r	EŒÊæCÀýýƒ(ãv8¸ItS)rµ q´Ëþfhkzƒ†Â~’û³áðÊÏêCû3EEßs¹2Ýlo=E›PÍ>3ÍÐž—”qÑ;žËxV› 6€ðV›’½68:“VQ¯qAÿ[É ¬ú¢åWÒàƒŸÆ¯ÏÈ™äˆ‚JQGð\õÿ“þžžŽÔr¹E}ëÍ•ó°Se:^tÂ4\HÌ‡lÒ	&>“#*‹~}Ö’>y«}æàèr2Ùqÿ¨þ³å³§QääääääoGÉ_h²"¯	ßQ§¡^5åÊåÍš'Y¤‹ƒWç“ÓÑEo*æ_‘»eÐÊ^$?Ù8neü”—ß0ù+ØAÀ@` 0ÞQ¦w XÎò£2T.™åp\ydkÆ Ðh4šÍ€f@ó'…foi¼ëûÅ±½À@` 0øS#°íþB/j>tA£â–/tð—Ž.F¯åé?ýÔðãë|Éê™W$x‘¿?»SPHáïÈg ]€.@ Ðèî&èúAþ€ê•«½À_à/ðøüþ~bü•t€ÛeÛ$Ü¤ªVn-zgõ½[Eþ£¬îÞ³‹¼ï,¡¯Ru.ü˜z‘tþñ.J‡‡‡‡‡‡‡ùç9ŒÌ§;sÖLæ¤urm'‚z»$áá"&#;@o@o@o@o@o@o@o@o¶Þðr5nCýábD(h(_’«V»p[fì Ôã…£:nÀ~‰*RþÁÉñ/c¶µd&õ`³5ßSý©7b§zKêYk|„¾§3ò¿þû:‘ÙSíËüdÓ"#ËðçÏuåXÄo7PÐùSbbbbbb±ÃÄbmŸ%í]¯O¡ÔÛ2ùnf$g^Ð®âgÞéñN•`oIÐÐÐÐÐÐ™–Î$Í‹'ä€º¾Ùut“´ªÝÒá.î†ƒÓñt:z=öReB.œ3XßAÈeÄè„Â a€ðn‚plŠÛ$ÛðJ‡»ðÛ	 í-ëÚ)Þf³ÙÀl`60ûc6ïWpgýä´u’Ü¯ XAöeÁ¬FUXs5ZÿÃpøœÊÎß½ÚZI)³ëù·¸ž2ðŽòAÃæê¯dNÈÝ©ÚÑ«/AŽ@Ž@Ž@Ž@Ž@Ž¾9r´yõeU¶¬½Ä0ƒÁŸƒÛ[2N¥Û[Ð«ž¤I×_¦3ßa›F 0?—qƒ/ã(
…Â@a ð'Fáßç“ÃóÓ7'Ç£³Ã1£î\¦hH°ÐE¡½eÌ#9$KP°4ÅÚ“÷jg wu&EF‰ÿ`âúúÌº8?Îwoý)[šédÂsØ9Ÿ,y½À¼ú²Ïfs˜º+î«;NÏ.OÆÓq3aÎ6måÌ¯Äqÿ·ãX‘Øþ‘ªS•°£úäµó–<ÄÈ‹ÈþšÄö6	¼¼¨Á[¶ÄÙå`²¡Ù—ª¸¸¸¸¸¸Ícä6÷3F5™”qûšýú!sâ&§è`RËf?‰uÇ-_ ¡ÇFÞkÇjwƒa-UZ#M/Rçèv¨Ú‘‚<¸w<g´ƒµœ~•²É±±É·±1˜[CZnDU§^Ïy›¢¸bMÔQ¹xb]Ì|`WPyÅÿºR*XÄ‰Üó­>¹Æª¥ÚÄ§º4†šVs˜*ADÑ“Ç›!¤¡é¯×ìÐ½;en“òBËŠÎ½?]Œ{¼Cåf1Ž7ƒª˜Ý Ky(òÝ f f f f f f;JÌ„]<ÌÌ>9¿ññññññññù*ÄÇ¢k³“…ùŒÏÖæY­=]³åÝ}ºóÖñ‚ºcÓs6.Úð ´´´´´´¦Kk¨ƒº»|Hœj|¥v¥\ÜÂ}­3Wãž²É”ÍŽ±³³/``````;Å(æÁ‡ûžÉz<Ú‚‚ÇC`=`=`=`=`=`=Ÿ€õ°£ÔÌçW“1¦¯“UÙÈ9A…@…@…@…@…@…@…@…’MZjô^Û)µþFýHBØçe8ÜY£æÿ2k›–lÀ›Àù:j‹+Dºð–Ucg|S*¬nz8µä<<<<<<a÷xBöÑ,!»—#d›Bî¬±+gÿ;n€©``````_šÈ'VîÎHŽël@>z¶’º­íF×Âs )H
$’IwI×õ5p8
ŽG£ÀÑÇQî…ÑjœMØl6›€MÀæc‡Íßç“éxòëñáX/ŽÏÏ¦¾Q¢¯È•šï’\UŽåJ×ù*ö–äCI™SÔñ“ØT_zëù@V +È
d²>zd]¤¡ÑY¡‡|äV*Ç#›‹É¼S‰Äv=ÛNu¾É)ÖTÇs÷“1.NŠ¦kÒ¿dŸA)ž»þ‚«LÊ7âš¶W5ñB+Ã¾aç¬+yKÃ_¶c\±5l´šS<c°SÏrñ{eÖY•ÅU98;?ËWC¥ÎT"EŠŒÀ4‘6uZ"°Áµj)² ‘Ñú¶ÆPtbäZ?:KÚŒªkU´ÎÝ(”×ìØ\HÞ¾¸œþZ´|æñ?È;oÈÙc~‘+)3¶áæ=”_¬W[zc*wuFCÂ®ÖæÆÄIyUº5úí2	ëvv×~¾µëäøˆLZß^šÝ5î¾öò·ýû®?°¨U]hÈ÷ª•càË±Ÿ€Õ‚ƒšówÍ‚üÚFÄt|¯ú”¤O`ÃL?ò5ò°BÓØÏþül¤~Æ	%b·.H/ñÅîGÙ]¸¿½fî!$,ËŽÉ¼QÖë“£ÖMà;š…ðk‹à™¸{y;ê.Q¢4¡ Uƒ–?OÕó;exÛâêÓÕg„†‹Ð?ÏûíáSÚr#£«„cÝ(M•›ù‹qœAk,ÑãÕüå.šŽ/¦½“»ëú«7]	ëÐ6X·Õ 6–+¤¤¤¤¤¤o§HÙ®Lycœš÷]Ê¡u‰$tÇé2ã%™ŸËç+ZUªSA~+!Ü*†á„¢8ÅSú·•cÙyÙDò³Õ,Ýìtíšk?Î*þêO°ŸÒ×˜MFZ"æúR¿)Ó…’¨.²æ4{_l¦m‡)vês0=ý:¾—eÞé™a›’q»í‰|ac1ÜØ»Y}yŽ¼‹Zº º º º º º`t?ng¿Ítj]MÎÓ8.¹ÇÌââõ<ä"jÅ–ÐzzÎ7-÷ ŸýkÒõ‘ašÙ‡Ò.w;:¬þ¦ó÷.ÙßêoADÕÎÿ.ÿ[ÍÇÿ®nE™¸lo©¶)õwõœŸg†”ƒ'“ÒÆÿ”ý–Y^´ èJÁ•ÆåBÜ#c¸û1ÕyÎyÿhH­„M3,—¡ñ¨sÆK×	%GôFôüå{‘üät2]Æ–iTô½,ŸST¥©'·ËÀ[2¸]ê‚ÿ’›P©þâ2bSSI-‘FÉ„4¹²þãí…“eY­Ùªv~7p ,ÍÙ½FAzKÅÞö‹…wnÑ'®på‰–¬‚›knÊÃžæV'ØzÎËÐr±(¹12¾[újÞ²ªý«kN+´ãgN1¬ 1D£ˆÁ·c+Wz3Ü”C)ö3gÅØvrç>Î«H)•s!·Xèjjjjj¸[ÔM{™À8þ‚«lÙX$€EX$ ÆÆÆÆÆÆ÷yß<3æ†ñýÄù®½8’tú¹ÃÆ¸²Ç§ãú,z ©<ý¤Ýk¨-<Ÿq/÷Ô‘™kr"ËçOŸ²Y7|ï-g'9µÞf­s3YÄvl>êwZ>Ù*„oñY9hhhhhhÛ.Ð¶ä7ßú6âCŽSxe–qX’7~¹"d¥t€QÀ(`0
Œ>~]7æJ{×ÍÞ’E’v
¤ÂØ,¤ŠâçììfIk‹o™Ðp«\_V$±©4	o…W!ð*Ä¶<Hü§wð*Äzâê^ÖfIrhìÑð’O®ûIÜ'[3ã¬ÝÕVÕöCõr;í‚¤«o€”eeZ\ÑÏ$k›ú›I†b‚b‚b‚b‚b‚bzüŠ‰\-oo#DBeÕ)Ðà7é	¾&olèjlû]ÄŒ‚Òü4:üåäü5#'çŽ%¢fÝ$aIç—t¡ÕÜ¤€|žœXˆ_Å	Âxö´eRVa˜Ü’´â?Õ¹{Õ‚ÙtD®y™³ÇáÐ¶V‘™¯­¶>³JŠ@
ÛG‰Üî·z¼z‡‚úFÌJ¨$1¸¸¸¸¸¸ÑNq£†ðÄP»(ûÚåÆ8B]›PêæµÉ‚1[?ˆŒnŠ¥2$O{xvZr¾ _€/Àà»Kà‹¬½ÕÝIpcª“rëGDï `¿Å0ÒYÙÑá¥³þj=ß·9;<ÚÅ£]<ÚÅ£]<Ú}lv	¬§Çg#þÄÞû:n­ƒ[”Ý‡êÜnÓcV™kJË™Ph³˜·;*.]¬¿‘üJræ×nÿ8×fK¹!À À À À¶]€ÊƒÊƒÊƒÊƒÊƒÊï•¿Â°&ò¿Ðh<h<h<h<h<h<h<h<h<h<hü¶Óøb©okA ñ ñ ñ ñ ñ ñ ñ ñ ñ ñ ñÛMã“¼àe¯Í·Zã¹ºHÙËÄg{´¹EEèxí¬ÑÞò]§—Ö2ÂKnüAÈÒ~3T“9(ÜfIJ¾Ú¸`=–É‡Å¡rÊÊ3Tfk3º%Ò,¥!1 1 1 1 1 1 1 1 1 1 1¾¶Ä :Ý«0ìµÏ,0ÊÔg·kdDlŒÏ­ÎL^ýíë6˜@J@J@J@J@J@J@J@J@J@J@J|u)ð÷ëØù|sÓl«øFÎt_¥ÃÞÏ™P;¦µ»	QÅP¯=xˆvqûH¯Î'§£‹ÞÔí¥Ý~Ór‘|ncXB¬&¿±`NWÁrw”¼}’ÂÂÂÂÂÂ¸+„ÑöŠ¸ðð‘Ž&w»ZE‰O¶w3ÅœöÓ¢Q¨@T ê"jgö¥;PƒØN€ˆOÌÕ¾ùû³µ‰›Ù sžLdOK±í+dƒ„R®b
S0˜‚aaaaaaüvc›È=ðØîŸ{bgMMMMMMMMMüvhbC£$ ÜyX§+ãzw–¢»[*Mø»S>{¾Ð´/A3nwfæT™eï(KÒ*‚yÚ[òý1óÔ¡áìØ·Ý÷²¤ÑdXðJððððððâ;9±¿Í†)±ú‘²Ÿf•ðËLnÙ1`x|p˜U“•öƒ]~0‰L½¾*Š¨¡y0:³2Ú·¯xöcó¡àîìó0/£¸rWó°x£…f˜Q Q Q Q QØA¢°y>¤]n5ÿáÂŠoHk‡ì_¹­vÖèÆ*kR?ªxEÃ†"ì÷j6™)5±ÆäÃS:<¥!BÏð”ÁìˆNÙo†Ëñ9Ÿ§Ühòñ˜ŽÇ±§S>xCo²ä&`»ÚôS:E¾ûW«/Âf–oªœû½£D¶Ÿµ‘Që®µöµ n§JR»ýAIƒ•†ª{0)nšS7¾¯nœÙº<8à>ÇdW¢iÙñ×k((((((ÙÎP2·jý³-gÂXÖ„eM Š Š Š Š Š Šß"UtS_-–èÎ´‹ÝS'Fßt÷|%ãÍ³äCñ!/ü+qT Ýg€Üžƒ‹ãÓñƒ¶x=`Þ^—|EËN¼äÏƒ8È—{êÈÌ5ùãéó§OÙQ‰¨¬÷Mï-g'9q6Kòÿ„"]•›É¢ ^›3Â¶ºØVÛêb[Ý­Fn|˜%ax¥½ë/2Ó¶¸O¶&úÈY»«mÑNåÞˆUÜ´½æ ]#©âµ©YV¦…`*ýL2h#h#h#h#h#h£ÝÒFk«&¦ö"˜xyð\ñÓj
MÆ¾#BõÑí)PG)×¯Ü3›ßÃÆm@[ -Ðh´Ý-´-Ã°™„,y&å¾­¾­KƒÃÂ)dòÒÛ,(ê‘à•YFnE‘H"NÊ`\ûïWÑÃzÎuó\k»œy!1@¶‡•^ehÈØvÓž2ñJ‡¹Lq q q q qØ-â/]×µøÛÜ­ÎkîÐa9UÍZñ Ì·_ë´ÿdˆ2»°/.¸× ©©üÄ­È4Q »œ-§bsqà3ðø|>ŸwŸ¿o÷ÿƒÒn‰–ä2¨K%³¼EW+” ã€qÀ8`0Œ&§zñ6;]¿ÿiv{*›„¦iX}¤,ÔeL`›å²o¢îþÑ¾Ðê Ë!ÑíÀv`;°ØlÿìØ^/Œì>:­o²Œ}¤Úcg}Kc·sKƒÃÕžxìq
ƒ”É™W*
ârí!¼gƒ÷lðžÞ³ÙÆ÷l 6 6 6 6 6 6 6>™Ú`;Ö"ãp©™æÜ7‡H)•Ìä.‡mxABBBBBBâ³	‰Uì}¬Ž!A^A·¼·wBU@U@U@U@U@U@U@U@U@U@Uì¼ª(›Í'¥¸ò‘SÈò¹ÓÚ°ÕWÜ!‘l+>$Ô‘Gxæ»:Ùæ/¥Bs@s@s@sl£æÀ(hhhhhhŒO£1rŠ¦yp!.ÐìÈ ìÈÑ¹2nÎ64œ32ä#5"rvŽÓvtÅg¦Ì_EN‰íí£ñôâøltq|~ö¾nbF¿hµT¼•Ì‹L¯Õ‘Û}n?ohÛÖrÀ:rs&Öº	È
È
È
È
È
ÈÊN‘êàš…¿¦-ìÃÝ¹·b†3œ˜áÄç¶Ïp‚ÍƒÍƒÍƒÍƒÍƒÍï›oÍ*þ#>ßI
FFFFFFFFFFFÿÕý²,x¤7D^vL©Š#îsˆãZ°q8ŠøäÛ]•°ÃæË M¥¶kI’]W±·$ŸKÊ: : : :`ÛuÀ#ñÁÅñéøÁ(þ,z ŽÏÐ¨íãuuæAäËú3¬<.Ÿ?}ÊKÍ\G•Þ[ÎNrjEÑ:7“EAl‰$$$$$$Ì.I˜Î†Î¦Ua¶z’„¬Úzß¤ÌŸ“®Øð;•J“0ðØ{’‰®CP¤W–is¾Ôœ"F¾‘y­Œ_—/FIâÖÆ2A^]”lBóác²˜½à”ï	ùßKhz9k5M^’ryBkAkAkAkm»Öú:{Ó|ÃoHMJ.ß\œ;ÄkŸ†n5á‰·àWƒZñ ‚‚‚‚‚‚qç£{’µ¦ÿá§Hy(,¬ãÛ!° ° ° ° °°ù'tttttÅîê
ñ­ïeöWüÄHX‘›¯-(¾Ìîy78³kîeIZ×GS6vWÒÌG
vjÇR2'Z =Ðh´Úíwí“tmÕ|¸a¶­^€Ò]7/`l—$ärþ@•qÈÃp?I‹ý™›œÉúº›1ÙˆÉFL6b²qÛ'¿áeÌÝš…ÔÛŽÌ±²ššššššh]Q˜ð–]U$\Ê5÷î2ù*’Ô_S•ôÍ©õŠÔ¸‘ßýxR¥•È¡_ðU†š—3È$È$È$È¤­—IXôþ¿Ugø|Ý—tðaØ|njIl×ž–;°¿ý,µŽÊÔ'OƒƒƒÛz†oÔB7@7@7@7@7@7|BÝpE=]+‡ŸøÈúq?Šbix—ærÃžïåÞuZXM±·LŸ“™ÜApùyëÔÇ¦.)ù*òÂ$6Íažùó6Ç†Ùn)     °#ÁÏš—h&¥ëV· ò]êóeâ³IŒö–÷½L+ÙU¨;p¬³'Yê¢´ ´ ´ ´ ´ ´ ´ ´ ´ ´ ´À6hâ‘ä"AÞ¼@2®ÎÜÿÌ “ý ø ø ø ø ø ø ø ø ø øÛIðçeëM‚‹,X,Øüršº5]±!¸7IXFÂZÄPB)AëAëAëAëAëAëAëAëAëAëAë·‚ÖËý"«xê} 0Ë9 9 9 9 9 9 9 9 9 9°r€Ê1¹gjAðF&o\»c_â­7ÂZ°|°|°|°|°|°|°|°|°|°ü-eùT/
8fÃrw¥ZÌÓ™ÑÏÉ»í|3ÈªÍ·SMþôähtöz<9¿œîSó¨µyI¹¬’R•1Qfù„^µáyeY©µmÞwÛ"Ê¨Ñ
3¬ßõ¥Þ¿Ê’kªÇ$Îj©3Ÿ¢‚jjLÎ`m)ˆ>ˆ>ˆ>ˆ>ˆ>ˆ>ˆ>ˆ>ˆ>ˆ>ˆþ¶}úSý®Þ¾ùû³Ö—ÁfuUB4ûÔÀ~ÎY*g¿©C&{R,ùs ¾N™ðwBeÿù# Ö¸¿0í5 5 5 5 5 5 5 5 5 5°-j€¼­hMûóaåtTëáœbDÂCV=	j@e–qÔâ“ ÷ ÷ ÷ ÷ ÷ ÷ ÷ ÷ ÷ ÷ ÷ÛAïó”FÍî§|dý{õƒÜƒÜƒÜƒÜƒÜƒÜƒÜƒÜƒÜƒÜ[äÞ.ÕÙðzo›¡TÈ®õÁžý ÷ ÷ ÷ ÷ ÷ ÷ ÷ ÷ ÷ ÷[OïÉ×‚ùê!zo‰uíø²7¬±}áY û û û û û û û û û û û[C÷}mx}³P§HÒNT›¥Zv/„Röäûwø³Ø4444Ëiþ\‘qq˜%ax¥½ë/2Ÿ¸Ï{¬$kwµU—{#VqÛk]…‚˜F|V¦… ýL²¶©)¾\ŸŽÄ†gÑèÀ»MµLÕ4V˜q/÷Ô‘™k2.‡ƒçOŸ2p“u×±ª÷–³“œªýÓ’2¯s3YÄn;5#####£FK}[‹¢ËøŠó%AÒƒ|Q·E…«@Gø|»(ÿ¹åÈ7-Õ¾EAÚÚÚÚÚÚ¶´-ñî,[iWªü{veŠLïklÍ‚´¼×®ƒ,êÐŸo=?°Š‚Å² /Óù’§—2s•$fÃ1ŽÙpÌ†c³á						_$”1µæ&àj5ë^–eÁ£_îj%°´d#7R@ö¦¼(ÑÓO"eÊ®KF²ÙM+7jØc™'†šššššššššššù"jæ÷ùd:žüz|8V‡çg¯Ž__NFÇçg,fÖ¤ŽõˆZå:¡Ar»qá
X=X=X=Xý²ú¡…'Ûj÷düÝE-.*¯4k¨Zò$K4äx‘d6–„!éª³¿öÛGSI!Ù˜Þ›,‰RG¨ŽvŠ†¢iREC£}îå’ù)‘™ëÉÅxüõ>pª.qÄ¡Ÿ4 Û§žžâòÜ´×æ‘¦ç…p+¶¸5µ5)s&7Ó$NöJ‡y›$U9LÆÓóËÉá¸×¤B³Ž¥•~ ÖÒdfjr¤5·¾èµÏnAçÜ·X¥¦¯ˆš¨4!ûYªµÕÖ»8}ÖÜÐJjC_‘e¥yÌRÂj¨nð4Ç©¾kMçrnxŽOßœŒ.Æ÷ºjå R8û%G+2|óÕK¢²V¤*Çþ(g††††††~Üz¸+y}‚°–ä=’Ã…IdéÒñÆÖäèûIZìÏDŠÍ–¢xœááááÿù„kEï²µ4c³S8ÝÝ©¹•ZÿHªB+A+A+A+A+A+=~­dË]×Õ‚iì;þ²±kmùñá†ÉÌ@DFI&[US’2ùp®(ÛT³áx¾¶z!Ol…«ùñœ's"Õ|¯|ÿ›Â¨WHeE´´ŸW¹§3¿wdÿÚzS^2‹
b)h°É9ÛsÓ—Ò›Ø¿í,d~>6,ž6µ–:Ií{‘¿?»kÎ™%f±ffffff±CÌbašÿ¾6Ö{2}ËêÞð0“¹
ol}OoS·Þ&™%J˜©ÄL%f*·~¦’C›[*doã<;FÒ¬Ž@|5ÍxÁO+VÏžg< ë€9°}ïÛwËhôÌé
6í¡I¦0‘ ^r|úf<™žŸñú“ãæ<…€Ô8e!Á•ÀqÊ…ÜvOJM¸+G§½ÑÀaA"-z¹÷î…»¿¢ó<ëËmvý½¢f”¯Ù0ày¬©#ê8¶pÄldàF]ÁÃ<Œx„HÞ’[µ)F^ßüòöãôÛ_^ˆõ_¾ûí·©Ó/ß½»«_¶¯ÁTªe·*¬_äïÕ!+ššW±uj"ÅðHI«fˆuÚ[êxá$±ˆc§œB¹Í‚‚•vÂ3!Yw0ç9xªÆE™¬Îç­í²¹¹£nëñ›ŽÿüÌÝ^desw¼~3'd*Îjá‡þóœˆÉÒ ’Cn\®’ýöô]“òMfÈ=î¦…°‡°‡°‡°‡°‡°ß!a/|uý™ÁQ“è^u»×}1¤ûø ­äíÒîƒWç“ÓÑEo*½±"ï‘=ìEr›ÃXÂÉ‘üÆ²ð«Î”þ£¤á      »B3¨bA<OäõŠäÕòêÓ©GÉTA–«´”'úÎcëwª¾!tºÝ€n@7 ûñCwÞzô?•ÎÞÜŸu·ãé>žîãé>žîoûÓ}<0ÆãOñÀx¨}ÿ`tÄ±UFId„°ÉüZ¡Ú‰hÙÎ¤õNrßMH÷%¶W§Ä¹DýÉ5„RÆ±6^¨OŠ’y™ÒÅOO™Ê¢{‡uT™ö6çzp|v4þ¿‚3›’·+èªæ"ÕW	I\Õ„mnQ¼þÑ¯4Éˆ±ßðæ7§ç¿ŽïöºŸ%©šgItOÇ3É¬D´r•„¿ŽN.-°7
rXOæÜœ‘î8R!&)0II
LR`’“;4IQÆíiŠËØv¸ˆb¼‚€I
LR`’“˜¤ØáIŠÇ©Å¡r¡r¡r¡r¡r¡rw@åÊ&éÌ•ÃÖ†ë–®sí)(Iûïïn```àmÀ_çÓøˆÆ§üˆTTTTTTÚãWin;Ôªûš¯5²rÛ$£êý`y÷H2h4à'ðø	ü~?!~îMVçoø“ÇSù=ÕèdzNùLÄŒªOž}'ÇdÛ‡T¶Ò­£Ë‹ŸÏ'bBFûLòÍ$­6øˆt`'£ê2Vi½$¢à¶ÚëýÖÐê   0707010005c871000081b40000000000000000000000015ab21754000003bf000000660000000000000000000000000000002400000000root/usr/share/man/man1/svcmon.1.gz   ‹      mUÝoâ8÷_1}Ê]ÑöîáNØ–S‘
A„]í©íL2$Ñ&¶×v€¬V÷·ßØ@Û}Hâùô|üf® þ|7p¿}¸ýó¹ÿáwzþ`aü ‹É|ÊÌ.©¤€>D
éÏPØÀ€A½+4Pc¹p'úÚÚ€Æo5[ˆYU\¤¡÷ÿ½ˆ–ñ,fáGhÝ²'xÞÎ¢åz-âçí^¼âý4¾[Í<—ÍØ»Û±ÕÜX]'¶ÖØó2!S„Â  jîo6±XuÁÁ¾°¹Óx•F+.KLCXŸ<U¼\Û©¾Ï´Õ~Þ~lÓp‘+-3Í+úÂWZdTL!)kújº>=;k‹ÕVæ.šÏ'‹û˜±¿Jž™ˆ jE_	¤™nš–úÊ½ ÓáÄ?ÒW {®ÝiI–‚ÂÙ¦Sb]lµüŽNü@‰<¥p|¶RŸBUò+W!E_*[T¼ôñ;ÄÂõÒ5¯ßß¡vÎ™Éåþ”:aâÈQW›c¾x(lg—÷È6ÇRµ†6§®9*4†:òÞ¢ŸÈRêÑ]ô­˜?ß)èÚªÚ† ¤1Å¦DØñ’ \#€×VŽ2¢l¸¡úSDÖ6'¤öõ€—{Þ˜šQ{„“ë¤,~9ò‡NpŽÈ•m´ˆî§¬ƒŒ•TD
Ÿ°Oœf[’Z0
“b[P¥LxÙAÖr!A$ìÜï]‰öÜ&9Ó¸¥ˆsoF¸—º"hS..®†´hÔ9^ž˜-ÖÓÕçÉcïB>:1™‚£û…l½ïÓ=ïýXNVóibçð”QÇd“<KL,¡	®ÎÁÓÕËÐÑã§¡A5îÈ— ö9j0Ö‡à*ð¥ H.lfm¶íHKíU½› ä³ZLA/€huakŽü› h¨^œÂ'±„¬”‡—SZ‚4þ†ŠÌÇC©ÆC¸ñ9€K•WÎO"]¤ãApÉÉ´¬ÕžÓ
‡_±ÿDõ•À±¥úù]@0v¯áÈý{téñ¿ ]‰3ªG;@ŒM¼R%š¶1×©0½œ_ßp¥BÚHs7¾¼¬PƒËÍ×AK^]ãÖ\Š)ÑiÏ­­ý`»¹(´[{´œi`¿ÕÒ¯é7]§A¢óÎí ¶´!¨÷ÓáÂ¯ÿ'™NaòGÒ•Ÿ¥*ÓðËí¯žv;ùIž=I¦“Oë‡h³î‡b ¥K©M¨Û?§A ‡HZ¯—?ú›QMÈþ9\R8   0707010005c872000041ed0000000000000000000000045ab2183a00000000000000660000000000000000000000000000001700000000root/usr/share/opensvc    0707010005c873000041ed0000000000000000000000035ab2183a00000000000000660000000000000000000000000000001b00000000root/usr/share/opensvc/bin    0707010005c874000041ed0000000000000000000000025ab2183a00000000000000660000000000000000000000000000002000000000root/usr/share/opensvc/bin/init   0707010005c875000081b40000000000000000000000015ab2175400000255000000660000000000000000000000000000004000000000root/usr/share/opensvc/bin/init/darwin.com.opensvc.svcmgr.plist   <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Label</key>
	<string>com.opensvc.svcmgr</string>
	<key>ProgramArguments</key>
	<array>
		<string>/usr/share/opensvc/bin/init/opensvc.init.Darwin</string>
		<string>start</string>
	</array>
	<key>RunAtLoad</key>
	<true/>
	<key>StandardErrorPath</key>
	<string>/var/log/opensvc/svcmgr_boot_stderr.log</string>
	<key>StandardOutPath</key>
	<string>/var/log/opensvc/svcmgr_boot_stdout.log</string>
</dict>
</plist>
   0707010005c876000081b40000000000000000000000015ab2175400000831000000660000000000000000000000000000003200000000root/usr/share/opensvc/bin/init/opensvc.agent.xml <?xml version='1.0'?>
<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
<service_bundle type='manifest' name='site/application/opensvc'>
  <service name='site/application/opensvc' type='service' version='0'>
    <dependency name='multi_user_dependency' grouping='require_all' restart_on='none' type='service'>
      <service_fmri value='svc:/milestone/multi-user'/>
    </dependency>
    <property_group name='startd' type='framework'>
      <propval name='duration' type='astring' value='transient'/>
    </property_group>
    <instance name='boot' enabled='true'>
      <dependency name='opensvc_d_zones' grouping='require_all' restart_on='none' type='service'>
        <service_fmri value='svc:/system/zones:default'/>
      </dependency>
      <dependency name='opensvc_d_postinstall' grouping='require_all' restart_on='none' type='service'>
        <service_fmri value='svc:/site/application/opensvc:postinstall'/>
      </dependency>
      <exec_method name='start' type='method' exec='/usr/share/opensvc/bin/init/opensvc.init.SunOS start' timeout_seconds='0'/>
      <exec_method name='stop' type='method' exec='/usr/share/opensvc/bin/init/opensvc.init.SunOS stop' timeout_seconds='0'/>
      <exec_method name='refresh' type='method' exec=':true' timeout_seconds='60'/>
      <property_group name='boot' type='application'>
        <propval name='firstrun' type='astring' value='todo'/>
      </property_group>
    </instance>
    <instance name='postinstall' enabled='true'>
      <exec_method name='start' type='method' exec='/usr/share/opensvc/bin/postinstall' timeout_seconds='60'/>
      <exec_method name='stop' type='method' exec=':true' timeout_seconds='60'/>
      <exec_method name='refresh' type='method' exec=':true' timeout_seconds='60'/>
    </instance>
    <template>
      <common_name>
        <loctext xml:lang='C'>OpenSVC agent</loctext>
      </common_name>
      <description>
        <loctext xml:lang='C'>OpenSVC automation and configuration management agent</loctext>
      </description>
    </template>
  </service>
</service_bundle>
   0707010005c877000081fd0000000000000000000000015ab21754000001cb000000660000000000000000000000000000003100000000root/usr/share/opensvc/bin/init/opensvc.init.AIX  #!/bin/ksh

DEFAULTS="/etc/default/opensvc"
OSVC_ROOT_PATH="/usr/share/opensvc"

# Include opensvc defaults if available
[ -r "$DEFAULTS" ] && . "$DEFAULTS"

case $1 in
start)
	${OSVC_ROOT_PATH}/bin/nodemgr daemon start
	${OSVC_ROOT_PATH}/bin/nodemgr pushasset
	;;
stop)
	${OSVC_ROOT_PATH}/bin/nodemgr daemon shutdown
	;;
status)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon running
	;;
restart)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon restart
	;;
esac

 0707010005c878000081fd0000000000000000000000015ab217540000033e000000660000000000000000000000000000003400000000root/usr/share/opensvc/bin/init/opensvc.init.Darwin   #!/bin/bash
#
# Starts the services driven by OpenSVC
#
# description: Starts the services driven by OpenSVC whose
#              autostart node is this node.
# processname:

PATH=/usr/bin:/usr/sbin:$PATH

DEFAULTS="/etc/defaults/opensvc"
OSVC_ROOT_PATH="/usr/share/opensvc"

# Include opensvc defaults if available
[ -r "$DEFAULTS" ] && . "$DEFAULTS"

case $1 in
start)
        ipconfig waitall
        echo "OpenSVC : Starting daemon"
	${OSVC_ROOT_PATH}/bin/nodemgr daemon start
        echo
        echo "OpenSVC : Pushing node information"
	${OSVC_ROOT_PATH}/bin/nodemgr pushasset
	;;
stop)
	echo "OpenSVC : Stopping services and daemon"
	${OSVC_ROOT_PATH}/bin/nodemgr daemon shutdown
	;;
status)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon running
	;;
restart)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon restart
	;;
esac

  0707010005c879000081fd0000000000000000000000015ab217540000023c000000660000000000000000000000000000003500000000root/usr/share/opensvc/bin/init/opensvc.init.FreeBSD  #!/bin/sh
#

# PROVIDE: opensvc
# REQUIRE: LOGIN sshd cleanvar
# BEFORE: 
# KEYWORD: shutdown

DEFAULTS="/etc/defaults/opensvc"
OSVC_ROOT_PATH="/usr/share/opensvc"

# Include opensvc defaults if available
[ -r "$DEFAULTS" ] && . "$DEFAULTS"

case $1 in
faststart)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon start
        ${OSVC_ROOT_PATH}/bin/nodemgr pushasset
	;;
faststop)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon shutdown
	;;
status)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon running
	;;
restart)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon restart
	;;
esac

0707010005c87a000081fd0000000000000000000000015ab21754000001e4000000660000000000000000000000000000003200000000root/usr/share/opensvc/bin/init/opensvc.init.OSF1 #!/usr/bin/ksh

DEFAULTS="/etc/default/opensvc"
OSVC_ROOT_PATH="/usr/share/opensvc"

# Include opensvc defaults if available
[ -r "$DEFAULTS" ] && . "$DEFAULTS"

case $1 in
start)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon start
        ${OSVC_ROOT_PATH}/bin/nodemgr pushasset
	;;
stop)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon shutdown
	;;
status)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon running
	;;
restart)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon restart
	;;
esac

0707010005c87b000081fd0000000000000000000000015ab2175400000299000000660000000000000000000000000000003300000000root/usr/share/opensvc/bin/init/opensvc.init.SunOS    #!/bin/sh
#
#	/etc/init.d/opensvc
#
# Starts the services driven by OpenSVC
#
# description: Starts the services driven by OpenSVC whose
#              autostart node is this node.
# processname:

DEFAULTS="/etc/default/opensvc"
OSVC_ROOT_PATH="/usr/share/opensvc"

# Include opensvc defaults if available
[ -r "$DEFAULTS" ] && . "$DEFAULTS"

case $1 in
start)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon start
        ${OSVC_ROOT_PATH}/bin/nodemgr pushasset
	;;
stop)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon shutdown
	;;
status)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon running
	;;
restart)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon restart
	;;
esac

   0707010005c87c000081fd0000000000000000000000015ab21754000002d3000000660000000000000000000000000000003400000000root/usr/share/opensvc/bin/init/opensvc.init.debian   #!/bin/bash

### BEGIN INIT INFO
# Provides:             opensvc
# Required-Start:       $all
# Required-Stop:        
# Default-Start:        2 3 4 5
# Default-Stop:         0 1 6
# Short-Description:    OpenSVC services startup script
### END INIT INFO

DEFAULTS="/etc/default/opensvc"
OSVC_ROOT_PATH="/usr/share/opensvc"

# Include opensvc defaults if available
[ -r "$DEFAULTS" ] && . "$DEFAULTS"

case $1 in
start)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon start
        ${OSVC_ROOT_PATH}/bin/nodemgr pushasset
	;;
stop)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon shutdown
	;;
status)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon running
	;;
restart)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon restart
	;;
esac
 0707010005c87d000081fd0000000000000000000000015ab21754000003c2000000660000000000000000000000000000003200000000root/usr/share/opensvc/bin/init/opensvc.init.hpux #!/bin/sh

PATH=/sbin:/usr/sbin:/bin:/usr/bin
export PATH

DEFAULTS="/etc/rc.config.d/opensvc"
OSVC_ROOT_PATH="/usr/share/opensvc"

# Include opensvc defaults if available
[ -r "$DEFAULTS" ] && . "$DEFAULTS"

case $1 in
start_msg)
	if [ "$RUN_OPENSVC" -ne 0 ] ; then
		echo "Starting opensvc daemon"
	fi
	;;
start)
	if [ "$RUN_OPENSVC" -ne 0 ] ; then
		echo "Starting opensvc daemon"
	else
		exit 0
	fi
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon start
	${OSVC_ROOT_PATH}/bin/nodemgr collect stats
        ${OSVC_ROOT_PATH}/bin/nodemgr pushasset
	;;
stop_msg)
	if [ "$RUN_OPENSVC" -ne 0 ] ; then
		echo "Shutting down opensvc services and daemon"
	fi
	;;
stop)
	if [ "$RUN_OPENSVC" -ne 0 ] ; then
		echo "Shutting down opensvc services and daemon"
	else
		exit 0
	fi
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon stutdown
	;;
status)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon running
	;;
restart)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon restart
	;;
esac
  0707010005c87e000081fd0000000000000000000000015ab2175400000349000000660000000000000000000000000000003400000000root/usr/share/opensvc/bin/init/opensvc.init.redhat   #!/bin/bash
#
#	/etc/rc.d/init.d/opensvc
#
# Starts the services driven by OpenSVC
#
# chkconfig: 2345 99 01
# description: Starts the services driven by OpenSVC whose
#              autostart node is this node.
# processname:

# Source function library.
. /etc/init.d/functions

DEFAULTS="/etc/sysconfig/opensvc"
OSVC_ROOT_PATH="/usr/share/opensvc"

# Include opensvc defaults if available
[ -r "$DEFAULTS" ] && . "$DEFAULTS"

case $1 in
start)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon start
	[ -d /var/lock/subsys ] && touch /var/lock/subsys/opensvc
        ${OSVC_ROOT_PATH}/bin/nodemgr pushasset
	;;
stop)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon shutdown
	rm -f /var/lock/subsys/opensvc
	;;
status)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon running
	;;
restart)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon restart
	;;
esac

   0707010005c87f000081fd0000000000000000000000015ab21754000002d7000000660000000000000000000000000000003200000000root/usr/share/opensvc/bin/init/opensvc.init.suse #!/bin/sh -e

### BEGIN INIT INFO
# Provides:             opensvc
# Required-Start:       $all
# Required-Stop:        
# Default-Start:        2 3 4 5
# Default-Stop:         0 1 6
# Short-Description:    OpenSVC services startup script
### END INIT INFO

DEFAULTS="/etc/sysconfig/opensvc"
OSVC_ROOT_PATH="/usr/share/opensvc"

# Include opensvc defaults if available
[ -r "$DEFAULTS" ] && . "$DEFAULTS"

case $1 in
start)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon start
        ${OSVC_ROOT_PATH}/bin/nodemgr pushasset
	;;
stop)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon shutdown
	;;
status)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon running
	;;
restart)
        ${OSVC_ROOT_PATH}/bin/nodemgr daemon restart
	;;
esac

 0707010005c880000081b40000000000000000000000015ab2175400000201000000660000000000000000000000000000003e00000000root/usr/share/opensvc/bin/init/systemd.opensvc-agent.service [Unit]
Description=OpenSVC agent
Documentation=http://docs.opensvc.com/ file:/usr/share/doc/opensvc/ man:nodemgr(1) man:svcmgr(1) man:svcmon(1)
After=network.target network-online.target time-sync.target multi-user.target
Before=opensvc-services.service
Requires=opensvc-services.service

[Service]
Type=forking
ExecStart=/usr/share/opensvc/bin/nodemgr daemon start
ExecStop=/usr/share/opensvc/bin/nodemgr daemon stop
KillMode=process
Restart=on-failure
OOMScoreAdjust=-1000

[Install]
WantedBy=multi-user.target
   0707010005c881000081b40000000000000000000000015ab2175400000162000000660000000000000000000000000000004100000000root/usr/share/opensvc/bin/init/systemd.opensvc-services.service  [Unit]
Description=OpenSVC agent and services oneshot stopper
Documentation=http://docs.opensvc.com/ file:/usr/share/doc/opensvc/ man:nodemgr(1) man:svcmgr(1) man:svcmon(1)
After=opensvc-agent.service

[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=/usr/share/opensvc/bin/nodemgr pushasset
ExecStop=/usr/share/opensvc/bin/nodemgr daemon shutdown

  0707010005c882000081fd0000000000000000000000015ab2175400000466000000660000000000000000000000000000002300000000root/usr/share/opensvc/bin/opensvc    #!/bin/sh

# variables users can override in the defaults file
OSVC_ROOT_PATH="/usr/share/opensvc"
OSVC_PYTHON="python"
OSVC_PYTHON_ARGS=""

if [ -r "/etc/defaults/opensvc" ]
then
	# FreeBSD, Darwin
	. "/etc/defaults/opensvc"
elif [ -r "/etc/default/opensvc" ]
then
	# Debian-like, Tru64, SunOS and HP-UX
	. "/etc/default/opensvc"
elif [ -r "/etc/sysconfig/opensvc" ]
then
	# Red Hat-like
	. "/etc/sysconfig/opensvc"
elif [ -r "/etc/rc.config.d/opensvc" ]
then
	# AIX
	. "/etc/rc.config.d/opensvc"
fi

BASENAME=`basename $0`

case $BASENAME in
opensvc)
	#
	# Use me as the shebang for python modules to be garantied the
	# same python requirements than the agent are met (ie 2.6+).
	#
	# Example: #!/usr/bin/env opensvc
	#
	exec "$OSVC_PYTHON" "$@"
	;;
esac

if [ "$BASENAME" = "nodemgr" -o "$BASENAME" = "svcmgr" -o "$BASENAME" = "svcmon" ]
then
	unset OSVC_SERVICE_LINK
	BIN="$OSVC_ROOT_PATH/lib/$BASENAME.py"
else
	# exec from a service link: add the --service parameter
	OSVC_SERVICE_LINK=$BASENAME
	export OSVC_SERVICE_LINK
	BIN="$OSVC_ROOT_PATH/lib/svcmgr.py"
fi

"$OSVC_PYTHON" $OSVC_PYTHON_ARGS "$BIN" $BIN_ARGS "$@"

  0707010005c883000081fd0000000000000000000000015ab217540000a225000000660000000000000000000000000000002700000000root/usr/share/opensvc/bin/postinstall    #!/usr/bin/env python

import os
import errno
import shutil
import glob
import sys
import tempfile
import inspect
import time

try:
    sysname, nodename, x, x, machine = os.uname()
except:
    import platform
    sysname, nodename, x, x, machine, x = platform.uname()

p0755 = int("0755", 8)
p0700 = int("0700", 8)
p0644 = int("0644", 8)

postinstall_d = sys.path[0]
if '/catalog/' in postinstall_d:
    # hpux packaging subsystem executes the postinstall from dir
    # /var/tmp/XXXXXXXXX/catalog/opensvc/commands/
    postinstall_d = "/usr/share/opensvc/bin"
    lsb = True
elif postinstall_d == "/usr/share/opensvc/bin":
    lsb = True
else:
    # windows install or unix execution from a non-lsb tree (ex: /opt/opensvc/)
    lsb = False

if lsb:
    pathsbin = "/usr/bin"
    pathsvc = None
    pathetc = "/etc/opensvc"
    pathvar = "/var/lib/opensvc"
    pathlck = '/var/lib/opensvc/lock'
    pathtmp = "/var/tmp/opensvc"
    pathlog = "/var/log/opensvc"
    pathbin = "/usr/share/opensvc/bin"
    pathlib = "/usr/share/opensvc/lib"
    pathini = "/usr/share/opensvc/bin/init"
    pathusr = None
else:
    pathsbin = postinstall_d
    pathsvc = os.path.realpath(os.path.join(pathsbin, '..'))
    pathetc = os.path.join(pathsvc, 'etc')
    pathvar = os.path.join(pathsvc, 'var')
    pathlck = os.path.join(pathvar, 'lock')
    pathtmp = os.path.join(pathsvc, 'tmp')
    pathlog = os.path.join(pathsvc, 'log')
    pathbin = postinstall_d
    pathlib = os.path.join(pathsvc, 'lib')
    pathini = os.path.join(pathsvc, 'bin', 'init')
    pathusr = os.path.join(pathsvc, 'usr')

def make_sure_path_exists(path):
    if os.path.exists(path):
        return
    os.makedirs(path, p0755)

def logit(msg, stdout=False, stderr=False):
    curframe = inspect.currentframe()
    calframe = inspect.getouterframes(curframe, 2)
    try:
        import datetime
        timestamp = str(datetime.datetime.now())
    except:
        timestamp = '?'

    osvlog = os.path.join(pathlog, 'postinstall.log')
    content = '['+calframe[1][3]+']['+timestamp+'] '+msg
    f = open(osvlog, 'a')
    f.write(content+'\n')
    f.close()
    if stdout:
        sys.stdout.write(msg+"\n")
    if stderr:
        sys.stderr.write("error ==> %s\n" % (msg))

make_sure_path_exists(pathlog)

if sysname == 'SunOS':
    osvcfmri = 'svc:/site/application/opensvc:boot'
    cmd = ['svcs', '-vl', osvcfmri, '2>/dev/null' ]
    ret = os.system(' '.join(cmd))
    if ret == 0:
	# Solaris install from IPS package with standard FMRI
        logit("Solaris postinstall check for first run",stdout=False)
	cmd = ['svcprop', '-p', 'boot/firstrun', osvcfmri, '|', 'grep', 'done']
        ret = os.system(' '.join(cmd))
	if ret == 0:
	    logit("Solaris postinstall already executed. Exiting.",stdout=False)
            sys.exit(0)

logit("\nStarting OpenSVC postinstall\n",stdout=True)

SolarisRootRelocate = False
if sysname == 'SunOS' and "PKG_INSTALL_ROOT" in os.environ and os.environ['PKG_INSTALL_ROOT'] != '/':
     logit("SunOS PKG_INSTALL_ROOT <%s>"%(os.environ['PKG_INSTALL_ROOT']))
     SolarisRootRelocate = True

variables = {
  "pathsvc": pathsvc,
  "pathsbin": pathsbin,
  "pathbin": pathbin,
  "pathetc": pathetc,
  "pathvar": pathvar,
  "pathtmp": pathtmp,
  "pathlib": pathlib,
  "pathlog": pathlog,
  "pathusr": pathusr,
  "SolarisRootRelocate": SolarisRootRelocate
}
for key in variables:
    logit("var %s <%s>"%(key,variables[key]))

def install_cron():
    logit("begin")
    if sysname == 'Windows':
        logit("windows not applicable")
        return
    else:
        return install_cron_unix()

def install_winservice():
    if sysname != 'Windows':
        return
    logit("begin")
    logit("install OpenSVC agent winservice",stdout=True)
    schedstop()
    time.sleep(3)
    schedremove()
    schedinstall()
    schedstart()

def schedremove():
    logit("begin")
    cmd = ' remove'
    schedcmd(cmd)

def schedstart():
    logit("begin")
    cmd = ' start'
    schedcmd(cmd)

def schedstop():
    logit("begin")
    cmd = ' stop'
    schedcmd(cmd)

def schedinstall():
    logit("begin")
    cmd = ''
    cmd += ' --username LocalSystem'
    cmd += ' --startup auto'
    cmd += ' install\n'
    schedcmd(cmd)

def schedcmd(_cmd):
    logit("begin")
    logit("_cmd %s"%_cmd)
    rc = '"'+sys.executable+'" "'+os.path.join(pathlib, 'osvcd_winservice.py')+'"'
    cmd = "@echo off\n"
    cmd += rc
    cmd += _cmd
    fd, fname = tempfile.mkstemp(dir=pathtmp, suffix='.cmd')
    f = os.fdopen(fd, 'w')
    f.write(cmd)
    f.close()
    import subprocess
    subprocess.call([fname])
    os.unlink(fname)

def save_file(infile):
    logit("begin")
    logit("infile <%s>"%infile)
    if not os.path.exists(infile):
        return True
    try:
        import datetime
        timestamp = str(datetime.datetime.now())
        tmp = timestamp.replace(" ", ".")
        ts = tmp.replace(":", ".")
    except:
        ts = 'opensvc.postinstall'
    ofname = os.path.basename(infile)
    logit("ofname <%s>"%ofname)
    nfname = ofname + '.crontab.' + ts
    logit("nfname <%s>"%nfname)
    outfile = os.path.join(os.sep, pathtmp, nfname)
    logit("outfile <%s>"%outfile)

    logit("saving file <%s> to <%s>"%(infile,outfile),stdout=True)
    try:
        shutil.copyfile(infile, outfile)
    except:
        import traceback
        traceback.print_exc()
        logit("error while trying to save file <%s> to <%s>"%(infile,outfile),stderr=True)
        return False
    return True


def install_cron_unix():
    logit("begin")
    """install opensvc cron jobs
    """

    remove_entries = [
      'bin/nodemgr schedulers',
      'bin/nodemgr compliance check',
      'bin/svcmon ',
      'bin/cron/opensvc',
      'svcmgr resource monitor',
      'svcmgr resource_monitor',
      'nodemgr cron',
      'perfagt.'+sysname,
    ]

    purge = []
    root_crontab = False

    """ order of preference
    """
    if sysname == 'SunOS' :
        if SolarisRootRelocate is True:
            suncron = os.environ["PKG_INSTALL_ROOT"] + '/var/spool/cron/crontabs/root'
            root_crontab_locs = suncron
        else:
            root_crontab_locs = [ '/var/spool/cron/crontabs/root' ]
    else:
        root_crontab_locs = [
            '/etc/cron.d/opensvc',
            '/var/spool/cron/crontabs/root',
            '/var/spool/cron/root',
            '/var/cron/tabs/root',
            '/usr/lib/cron/tabs/root',
        ]
    for loc in root_crontab_locs:
        logit("looping crontab location <%s>"%loc)
        if os.path.exists(os.path.dirname(loc)):
            if not root_crontab:
                root_crontab = loc
                logit("identifying <%s> as root crontab"%root_crontab)
            elif os.path.exists(loc):
                logit("adding <%s> to purge table"%loc)
                purge.append(loc)

    if not root_crontab:
        logit("no root crontab found in usual locations <%s>"%str(root_crontab_locs),stderr=True)
        return False

    new = False
    if os.path.exists(root_crontab):
        try:
            f = open(root_crontab, 'r')
            new = f.readlines()
            f.close()
            logit("loaded crontab <%s> content <%s>"%(root_crontab,new))
        except:
            f.close()
            import traceback
            traceback.print_exc()

    if not new:
        logit("empty crontab found. skipping purge.",stderr=False)
        if os.path.exists(root_crontab) and os.stat(root_crontab).st_size == 0:
            logit("removing crontab %s"%root_crontab)
            os.unlink(root_crontab)
        return False

    i = -1
    for line in new:
        i += 1
        for re in remove_entries:
            logit("looping re <%s>"%re)
            if line.find(re) != -1:
                logit("delete line <%s> from <%s>"%(re,root_crontab))
                del new[i]

    logit("saving crontab <%s>"%root_crontab)
    try:
        save_file(root_crontab)
    except:
        logit('Error while trying to backup crontab <%s>. skipping crontab update'%(root_crontab),stderr=True)
        return False

    logit("updating crontab <%s> with content <%s>"%(root_crontab,new))
    try:
        f = open(root_crontab, 'w')
        f.write(''.join(new))
        f.close()
    except:
        logit("error while trying to update crontab %s"%root_crontab,stderr=True)
        f.close()
        import traceback
        traceback.print_exc()

    """ Activate changes (actually only needed on HP-UX)
    """
    if sysname in ("HP-UX", "SunOS") and root_crontab.find('/var/spool/') != -1:
        logit("crontab activation requested")
        cmd = ['crontab', root_crontab]
        ret = os.system(' '.join(cmd))

    for loc in purge:
        try:
            f = open(loc, 'r')
            new = [ line for line in f.readlines() if line.find('opensvc.daily') == -1 and line.find('svcmon --updatedb') == -1 ]
            f.close()
            f = open(loc, 'w')
            f.write(''.join(new))
            f.close()
        except:
            f.close()
            import traceback
            traceback.print_exc()

    """ Clean up old standard file locations
    """
    for f in ['/etc/cron.daily/opensvc', '/etc/cron.daily/opensvc.daily']:
        if os.path.exists(f):
            logit("removing %s"%f)
            os.unlink(f)


def activate_chkconfig(svc):
    logit("begin")
    cmd = ['chkconfig', '--add', svc]
    ret = os.system(' '.join(cmd))
    if ret > 0:
        return False
    return True

def activate_systemd(launcher):
    logit("begin")
    systemdsvcs = ['opensvc-agent.service', 'opensvc-services.service']

    for systemdsvc in systemdsvcs:
        _activate_systemd(systemdsvc)

def _activate_systemd(systemdsvc):
    # remove unit file from /etc/systemd/system
    olddst = os.path.join('/etc/systemd/system/', systemdsvc)
    if os.path.exists(olddst):
        logit("found unit file in /etc/systemd/system",stdout=False)
        logit("disabling old systemd unit file",stdout=False)
        cmd = ['systemctl', '-q', 'disable', systemdsvc]
        ret = os.system(' '.join(cmd))
        if ret > 0:
            logit("issue met while trying to disable old %s systemd unit file"%systemdsvc,stderr=True)
        logit("removing old systemd unit file",stdout=False)
        cmd = ['rm', '-f', olddst]
        ret = os.system(' '.join(cmd))
        if ret > 0:
            logit("issue met while trying to remove old %s systemd unit file"%systemdsvc,stderr=True)

    systemd_unit_path = [
            '/usr/lib/systemd/system/',
            '/lib/systemd/system/',
        ]

    installed = False
    for path in systemd_unit_path:
        if os.path.exists(os.path.dirname(path)):
            # populate systemd tree with opensvc unit file
            src = os.path.join(pathini, "systemd."+systemdsvc)
            dst = os.path.join(path, systemdsvc)
            logit("installing %s systemd unit file in %s"%(systemdsvc, path),stdout=True)
            try:
                shutil.copyfile(src, dst)
                os.chmod(dst, p0644)
                installed = True
            except Exception as exc:
                logit("issue met while trying to install systemd unit file: %s"%str(exc),stderr=True)

    if installed is not True:
        logit("issue met while trying to identify suitable systemd unit file location",stderr=True)

    # reset paths in ExecStart and ExecStop
    os.system("sed -i 's@/usr/share/opensvc/bin@"+pathbin+"@' "+dst)

    # reload systemd configuration
    logit("reloading systemd configuration",stdout=True)
    cmd = ['systemctl', '-q', 'daemon-reload']
    ret = os.system(' '.join(cmd))
    if ret > 0:
        logit("issue met during systemctl reload",stderr=True)

    # enable opensvc agent startup through systemd
    logit("enabling systemd configuration")
    cmd = ['systemctl', '-q', 'enable', systemdsvc]
    ret = os.system(' '.join(cmd))
    if ret > 0:
        logit("issue met during systemctl enable",stderr=True)


def systemd_mgmt():
    logit("begin")
    cmd = ['systemctl', '--version', '>>/dev/null', '2>&1']
    ret = os.system(' '.join(cmd))
    if ret > 0:
        return False
    return True

def activate_ovm(launcher):
    logit("begin")
    activate_chkconfig('zopensvc')

def activate_redhat(launcher):
    logit("begin")
    activate_chkconfig('opensvc')

def activate_debian(launcher):
    logit("begin")
    cmd = ['update-rc.d', '-f', 'opensvc', 'remove']
    ret = os.system(' '.join(cmd))
    if ret > 0:
        logit("issue met while trying to remove opensvc rc launchers",stderr=True)
        return False
    cmd = ['update-rc.d', 'opensvc', 'defaults']
    ret = os.system(' '.join(cmd))
    if ret > 0:
        logit("issue met while trying to install opensvc rc launchers",stderr=True)
        return False
    return True

def activate_hpux(launcher):
    logit("begin")
    rc = "/sbin/init.d/opensvc"
    links = ["/sbin/rc1.d/K010opensvc", "/sbin/rc2.d/K010opensvc", "/sbin/rc3.d/S990opensvc"]
    if os.path.exists("/sbin/rc2.d/S990opensvc"):
        logit("removing /sbin/rc2.d/S990opensvc")
        os.unlink("/sbin/rc2.d/S990opensvc")
    for l in links:
        if not os.path.islink(l):
            if os.path.exists(l):
                logit("removing %s"%l)
                os.unlink(l)
            logit("create link %s -> %s"%(l,rc))
            os.symlink(rc, l)
    try:
        f = open("/etc/rc.config.d/opensvc", "w")
        f.write("RUN_OPENSVC=1\n")
        f.close()
    except:
        logit("issue met while trying to install rc.config.d opensvc file",stderr=True)
        f.close()
        import traceback
        traceback.print_exc()
    return True

def activate_AIX(launcher):
    logit("begin")
    rc = "/etc/rc.d/init.d/opensvc"
    links = ["/etc/rc.d/rc2.d/S990opensvc"]
    for l in links:
        if not os.path.islink(l):
            if os.path.exists(l):
                logit("removing %s"%l)
                os.unlink(l)
            logit("create link %s -> %s"%(l,rc))
            os.symlink(rc, l)
    return True

def activate_OSF1(launcher):
    rc = "/sbin/init.d/opensvc"
    links = ["/sbin/rc0.d/K010opensvc", "/sbin/rc2.d/K010opensvc", "/sbin/rc3.d/S990opensvc"]
    for l in links:
        if not os.path.islink(l):
            if os.path.exists(l):
                logit("removing %s"%l)
                os.unlink(l)
            logit("symlinking %s and %s"%(rc,l))
            os.symlink(rc, l)
            return True

def activate_SunOS(launcher):
    logit("begin")
    if SolarisRootRelocate is True:
        rc = "/etc/init.d/opensvc"
        links = [os.environ["PKG_INSTALL_ROOT"] + "/etc/rc0.d/K00opensvc", os.environ["PKG_INSTALL_ROOT"] + "/etc/rc3.d/S99opensvc"]
    else:
        rc = "/etc/init.d/opensvc"
        links = ["/etc/rc0.d/K00opensvc", "/etc/rc3.d/S99opensvc"]
    logit("rc <%s>"%rc)
    for l in links:
        logit("link <%s>"%l)
        if not os.path.islink(l):
            if os.path.exists(l):
                logit("removing %s"%l)
                os.unlink(l)
            logit("symlinking %s and %s"%(rc,l))
            os.symlink(rc, l)
    return True

def activate_FreeBSD(launcher):
    logit("begin")
    return True

def activate_Darwin(launcher):
    logit("begin")
    return True

def update_file(filename, srctext, replacetext):
    logit("begin")
    """ replace into filename srctext by replacetext
    """
    import fileinput
    for line in fileinput.input(filename, inplace=1):
        if line.rstrip('\n') == srctext.rstrip('\n') :
            line = replacetext
        msg=line.rstrip('\n')
        logit(msg,stdout=True)
    fileinput.close()

def install_params(path2file):
    logit("begin")
    """ install template file with tunable variables
    """
    if os.path.exists(path2file):
        logit("file %s already present"%path2file)
        return

    try:
        f = open(path2file, "w")
    except:
        import traceback
        traceback.print_exc()
    else:
        logit("writing new <%s>"%path2file)
        f.write("# OpenSVC startup and wrapper configuration file\n")
        f.write("#\n")
        f.write("# You may need to adapt parameters to fit your environment\n")
        f.write("# This file is not modified during software upgrades\n")
        f.write("# If empty, default settings are used in the init script\n\n\n")
        f.write("#\n")
        f.write("\n")
        f.write("#\n")
        f.write("# Wrapper configuration\n")
        f.write("#\n")
        f.write("#OSVC_ROOT_PATH=/opt/opensvc\n")
        f.write("#OSVC_PYTHON=python\n")
        f.write("#LD_LIBRARY_PATH=\n")
        f.write("#LD_PRELOAD=\n")
        f.write("\n")
        f.close()

def install_rc():
    logit("begin")
    """install startup script
    """
    params = None
    copyrc = True

    if os.path.exists('/etc/debian_version'):
        rc = '/etc/init.d/opensvc'
        params = '/etc/default/opensvc'
        src = os.path.join(pathini, 'opensvc.init.debian')
        if systemd_mgmt():
            logit("debian with systemd")
            copyrc = False
            activate = activate_systemd
        else:
            logit("debian with update-rc.d (rely on insserv)")
            activate = activate_debian
    elif os.path.exists('/etc/arch-release'):
        rc = '/etc/init.d/opensvc'
        params = '/etc/default/opensvc'
        src = os.path.join(pathini, 'opensvc.init.debian')
        if systemd_mgmt():
            logit("arch with systemd")
            copyrc = False
            activate = activate_systemd
    elif os.path.exists('/etc/SuSE-release'):
        rc = '/etc/init.d/opensvc'
        params = '/etc/sysconfig/opensvc'
        src = os.path.join(pathini, 'opensvc.init.suse')
        if systemd_mgmt():
            logit("SuSE with systemd")
            copyrc = False
            activate = activate_systemd
        else:
            logit("SuSE with chkconfig (rely on insserv)")
            activate = activate_redhat
    elif os.path.exists('/etc/redhat-release'):
        params = '/etc/sysconfig/opensvc'
        src = os.path.join(pathini, 'opensvc.init.redhat')
        try:
            f = open('/etc/redhat-release', 'r')
            buff = f.read()
            f.close()
        except:
            buff = ""
        if buff.find('Oracle VM server') != -1:
            rc = '/etc/init.d/zopensvc'
            activate = activate_ovm
        else:
            rc = '/etc/init.d/opensvc'
            if systemd_mgmt():
                logit("Red Hat with systemd")
                copyrc = False
                activate = activate_systemd
            else:
                logit("Red Hat with chkconfig (rely on insserv)")
                activate = activate_redhat
    elif sysname == "HP-UX":
        rc = '/sbin/init.d/opensvc'
        src = os.path.join(pathini, 'opensvc.init.hpux')
        activate = activate_hpux
    elif sysname == "SunOS":
        if SolarisRootRelocate is True:
            rc = os.environ["PKG_INSTALL_ROOT"] + '/etc/init.d/opensvc'
            params = os.environ["PKG_INSTALL_ROOT"] + '/etc/default/opensvc'
            src = os.environ["PKG_INSTALL_ROOT"] + os.path.join(pathini, 'opensvc.init.SunOS')
        else:
            rc = '/etc/init.d/opensvc'
            params = '/etc/default/opensvc'
            src = os.path.join(pathini, 'opensvc.init.SunOS')
        activate = activate_SunOS
    elif sysname == "OSF1":
        rc = '/sbin/init.d/opensvc'
        src = os.path.join(pathini, 'opensvc.init.OSF1')
        activate = activate_OSF1
    elif sysname == "FreeBSD":
        rc = '/etc/rc.d/opensvc'
        params = '/etc/defaults/opensvc'
        src = os.path.join(pathini, 'opensvc.init.FreeBSD')
        activate = activate_FreeBSD
    elif sysname == "AIX":
        rc = '/etc/rc.d/init.d/opensvc'
        src = os.path.join(pathini, 'opensvc.init.AIX')
        activate = activate_AIX
    elif sysname == "Darwin":
        rc = '/Library/LaunchDaemons/com.opensvc.svcmgr.plist'
        params = '/etc/defaults/opensvc'
        src = os.path.join(pathini, 'darwin.com.opensvc.svcmgr.plist')
        activate = activate_Darwin
    elif sysname == 'Windows':
        return False
    else:
        logit("could not select an init script: unsupported operating system",stderr=True)
        return False

    if os.path.islink(rc):
        logit("removing link %s"%rc)
        os.unlink(rc)

    if copyrc:
        logit("copying src launcher script to rc")
        shutil.copyfile(src, rc)
        os.chmod(rc, p0755)

    if params is not None and not os.path.exists(params):
        logit("installing default parameters file")
        install_params(params)

    activate(src)

def gen_keys():
    logit("begin")
    if sysname == 'Windows':
        return
    home = os.path.expanduser("~root")
    logit("home <%s>"%home)
    if SolarisRootRelocate is True:
        home = os.environ['PKG_INSTALL_ROOT'] + os.path.expanduser("~root")
        logit("SunOS and relocatable install home is now <%s>"%home)
    sshhome = os.path.join(home, ".ssh")
    logit("sshhome <%s>"%sshhome)
    if not os.path.exists(sshhome):
        logit("create dir %s"%sshhome,stdout=True)
        os.makedirs(sshhome, p0700)
    priv = os.path.join(sshhome, "id_rsa")
    pub = os.path.join(sshhome, "id_rsa.pub")
    if os.path.exists(pub) or os.path.exists(priv):
        logit("either %s or %s already exist"%(pub,priv))
        return
    cmd = ['ssh-keygen', '-t', 'rsa', '-b', '2048', '-P', '""', '-f', priv]
    try:
        ret = os.system(' '.join(cmd))
    except:
        logit("Error while trying to generate ssh keys")

def missing_dir(pathd):
    logit("begin")
    if not os.path.exists(pathd):
        logit("create dir %s"%pathd,stdout=True)
        os.makedirs(pathd, p0755)

def missing_dirs():
    logit("begin")
    missing_dir(pathlog)
    missing_dir(pathtmp)
    missing_dir(pathvar)
    missing_dir(pathetc)
    missing_dir(pathlck)

def convert_svclinks():
    logit("begin")
    missing_dir(pathetc)
    svcmgr = os.path.join(pathsbin, 'svcmgr')
    if not os.path.exists(svcmgr):
        logit("%s does not exist"%svcmgr)
        return 1
    rcService = os.path.realpath(os.path.join(pathbin, 'rcService'))
    if not os.path.exists(rcService):
        logit("%s does not exist"%rcService)
        return 1
    for fname in os.listdir(pathetc):
        fpath = os.path.join(pathetc, fname)
        if not os.path.islink(fpath):
            logit("%s is not a symlink"%fpath)
            continue
        rpath = os.path.realpath(fpath)
        if rpath != rcService:
            logit("%s != %s"%(rpath,rcService))
            continue
        logit("removing %s"%fpath)
        os.unlink(fpath)
        logit("create link %s -> %s"%(fpath,svcmgr))
        os.symlink(svcmgr, fpath)

def move_env_to_conf():
    for fpath in glob.glob(os.path.join(pathetc, "*.env")):
        svcname = os.path.basename(fpath)[:-4]
        new_basename = svcname+".conf"
        new_fpath = os.path.join(pathetc, new_basename)
        shutil.move(fpath, new_fpath)

def move_var_files_in_subdirs():
    for fpath in glob.glob(os.path.join(pathvar, "last_*")):
        dst = os.path.join(pathvar, "node")
        if not os.path.exists(dst):
            os.makedirs(dst)
        fname = os.path.basename(fpath)
        new_fpath = os.path.join(dst, fname)
        logit("move %s to %s" % (fpath, new_fpath))
        shutil.move(fpath, new_fpath)

    for fpath in glob.glob(os.path.join(pathvar, "*_last_*")):
        fname = os.path.basename(fpath)
        svcname = fname.split("_last_")[0]
        dst = os.path.join(pathvar, svcname)
        if not os.path.exists(dst):
            os.makedirs(dst)
        fname = fname.replace(svcname+"_", "")
        new_fpath = os.path.join(dst, fname)
        logit("move %s to %s" % (fpath, new_fpath))
        shutil.move(fpath, new_fpath)

    for fpath in glob.glob(os.path.join(pathvar, "*.push")):
        svcname = os.path.basename(fpath).split(".push")[0]
        dst = os.path.join(pathvar, svcname)
        if not os.path.exists(dst):
            os.makedirs(dst)
        fname = "last_pushed_env"
        new_fpath = os.path.join(dst, fname)
        logit("move %s to %s" % (fpath, new_fpath))
        shutil.move(fpath, new_fpath)

def move_usr_to_opt():
    logit("begin")
    linksvc = os.path.join(os.sep, 'service')
    old_pathsvc = os.path.join(os.sep, 'usr', 'local', 'opensvc')
    old_pathvar = os.path.join(old_pathsvc, 'var')
    old_pathetc = os.path.join(old_pathsvc, 'etc')

    if os.path.exists(old_pathvar):
        logit("found old var %s"%old_pathvar)
        for f in glob.glob(old_pathvar+'/*'):
            dst = os.path.join(pathvar, os.path.basename(f))
            if os.path.exists(dst) and dst.find('host_mode') == -1:
                logit("file %s already exist"%dst)
                continue
            if os.path.isdir(f):
                logit("copying dir %s to %s"%(f,dst))
                shutil.copytree(f, dst, symlinks=True)
            elif os.path.islink(f):
                linkto = os.readlink(f)
                logit("create link %s -> %s"%(dst,linto))
                os.symlink(linkto, dst)
            else:
                logit("copying file %s to %s"%(f,dst))
                shutil.copy2(f, dst)

    if os.path.exists(old_pathetc):
        logit("found old etc %s"%old_pathetc)
        for f in glob.glob(old_pathetc+'/*'):
            dst = os.path.join(pathetc, os.path.basename(f))
            if os.path.exists(dst):
                logit("file %s already exist"%dst)
                continue
            if os.path.islink(f):
                linkto = os.readlink(f)
                logit("create link %s -> %s"%(dst,linto))
                os.symlink(linkto, dst)
            elif os.path.isdir(f):
                logit("copying dir %s to %s"%(f,dst))
                shutil.copytree(f, dst, symlinks=True)
            else:
                logit("copying file %s to %s"%(f,dst))
                shutil.copy2(f, dst)

    if os.path.exists(old_pathsvc):
        logit("removing old_pathsvc %s"%old_pathsvc)
        shutil.rmtree(old_pathsvc)

    if os.path.islink(linksvc) and os.path.realpath(linksvc) == old_pathsvc:
        logit("removing linksvc %s"%linksvc)
        os.unlink(linksvc)

def move_var_files_19():
    # node frozen flag
    old_node_frozen = os.path.join(pathvar, "FROZEN")
    pathvar_node = os.path.join(pathvar, "node")
    if not os.path.exists(pathvar_node):
        os.makedirs(pathvar_node, p0755)
    if os.path.exists(old_node_frozen):
        node_frozen = os.path.join(pathvar_node, "frozen")
        shutil.move(old_node_frozen, node_frozen)

    # service frozen flag
    for fpath in glob.glob(os.path.join(pathvar, "FROZEN.*")):
        svcname = os.path.basename(fpath).replace("FROZEN.", "")
        pathvar_svc = os.path.join(pathvar, "services", svcname)
        if not os.path.exists(pathvar_svc):
            os.makedirs(pathvar_svc, p0755)
        svc_frozen = os.path.join(pathvar_svc, "frozen")
        shutil.move(fpath, svc_frozen)

    # stats
    pathstats = os.path.join(pathvar, "stats")
    if not os.path.exists(pathstats):
        os.makedirs(pathstats, p0755)
    for fpath in glob.glob(os.path.join(pathvar, "stats_*")):
        fname = os.path.basename(fpath).replace("stats_", "")
        dst = os.path.join(pathvar, "stats", fname)
        shutil.move(fpath, dst)


def install_etc_path():
    logit("begin")
    p = os.path.join(os.sep, 'etc', 'PATH')
    if not os.path.exists(p):
        logit("etc/PATH not found")
        return
    try:
        logit("loading %s"%(p))
        f = open(p, "r")
        buff = f.read()
        f.close()
    except:
        logit("issue met while trying to read %s"%(p),stderr=True)
        return
    l = buff.strip().split(":")
    n = len(l)
    for op in (pathbin, pathetc):
        if op in l:
            logit("dir %s already present in %s"%(op,p))
            continue
        logit("adding dir %s"%(op))
        l.append(op)
    if len(l) == n:
        logit("nothing changed in %s"%(p))
        return
    try:
        logit("updating %s"%(p))
        f = open(p, "w")
        f.write(":".join(l)+'\n')
        f.close()
    except:
        logit("issue met while trying to write %s"%(p),stderr=True)
        return

def install_profile():
    logit("begin")
    prof_d = os.path.join(os.sep, 'etc', 'profile.d')
    prof = os.path.join(prof_d, 'opensvc.sh')
    buff = "if ! echo ${PATH} | grep -q "+pathetc+"; then"+"\n"
    buff = buff+"    PATH=${PATH}:"+pathetc+"\n"
    buff = buff+"fi\n\n"
    buff = buff+"if ! echo ${PATH} | grep -qw "+pathsbin+"; then"+"\n"
    buff = buff+"    PATH=${PATH}:"+pathsbin+"\n"
    buff = buff+"fi\n"
    if not os.path.exists(prof_d):
        logit("no profile directory found")
        return
    try:
        logit("installing profile in file %s"%(prof))
        f = open(prof, 'w')
        f.write(buff)
        f.close()
    except:
        logit("issue met while trying to install profile in file %s"%(prof),stderr=True)
        f.close()
        import traceback
        traceback.print_exc()

def install_bash_completion():
    logit("begin")
    if pathsvc is None:
        return
    src = os.path.join(pathsvc, 'usr', 'share', 'bash_completion.d', 'opensvc')
    ds = [os.path.join(os.sep, 'etc', 'bash_completion.d'),
          os.path.join(os.sep, 'etc', 'bash', 'bash_completion.d')]
    for d in ds:
        dst = os.path.join(d, 'opensvc')
        if not os.path.exists(d):
            d = None
            continue
        else:
            break
    if d is None:
        logit("no bash completion directory found")
        return
    try:
        logit("installing bash completion file src %s to tgt %s"%(src,dst))
        shutil.copyfile(src, dst)
        os.chmod(dst, p0644)
    except:
        logit("issue met while trying to install bash completion file src %s to tgt %s"%(src,dst))

def install_link(source, target):
    logit("begin")
    if source == '' or target == '':
        logit("bad parameters")
        return False
    if os.path.realpath(source) == os.path.realpath(target):
        logit("link already ok")
        return True
    if os.path.islink(target) or os.path.exists(target):
        logit("unlink %s",target)
        os.unlink(target)
    try:
        logit("create link %s -> %s"%(target,source))
        os.symlink(source,target)
    except:
        logit("issue met while trying to symlink src %s with tgt %s"%(source,target))

def install_pythonlink():
    logit("begin")
    if sysname == 'Windows':
        return install_pythonlink_windows()
    else:
        return


def install_pythonlink_windows():
    logit("begin")
    logit("before appending pathlib to syspath")
    logit(os.environ["PATH"])
    sys.path = [pathlib] + sys.path
    logit("after appending pathlib to syspath")
    logit(os.environ["PATH"])
    from rcUtilitiesWindows import get_registry_value
    logit("before reading installfolder in registry")
    try:
        installfolder = get_registry_value('HKEY_CURRENT_USER', 'Software\\OpenSVC', 'path')
    except:
        logit("issue met while trying to read path into registry HKCU/Software/OpenSVC/path",stderr=True)
        sys.exit(1)

    installfolder = installfolder.rstrip('\\')
    logit("installfolder = <"+installfolder+">")
    osvcenv = os.path.join(installfolder, 'osvcenv.cmd')
    content = '@echo off\nset OSVCROOT='+installfolder+'\nset OSVCPYTHONROOT=%OSVCROOT%\python\nset PYTHONPATH=%OSVCROOT%\lib\nset OSVCPYTHONEXEC=%OSVCPYTHONROOT%\python.exe\ncall inpath.cmd OSVCPYTHONROOT'
    logit(content)
    f = open(osvcenv, 'w')
    f.write(content)
    f.close()

def move_host_mode():
    logit("begin")
    hm = os.path.join(pathvar, 'host_mode')
    cf = os.path.join(pathetc, 'node.conf')
    nodemgr = os.path.join(pathsbin, 'nodemgr')
    if not os.path.exists(hm):
        logit("file %s does not exist"%hm)
        return
    try:
        fp = open(hm, 'r')
        mode = fp.read().split()[0]
        fp.close()
    except:
        logit("failed to read old host_mode. renamed to %s"%(hm+'.old'))
        shutil.move(hm, hm+'.old')
        return
    cmd = [nodemgr, 'set', '--param', 'node.host_mode', '--value', mode]
    ret = os.system(' '.join(cmd))
    if ret != 0:
        logit("failed to set host_mode in node.conf",stdout=True)
        return
    shutil.move(hm, hm+'.old')

def nodeconf_params():
    logit("begin")
    nodeconf = os.path.join(pathetc, 'node.conf')
    dotnodeconf = os.path.join(pathetc, '.node.conf')

    # reset etc/.node.conf (autogenerated)
    if os.path.exists(dotnodeconf):
        logit("unlink file %s"%dotnodeconf)
        os.unlink(dotnodeconf)

    if not os.path.exists(nodeconf):
        logit("file %s does not exist"%nodeconf)
        return

    try:
        import ConfigParser
    except ImportError:
        import configparser as ConfigParser
    import copy
    try:
        config = ConfigParser.RawConfigParser()
    except AttributeError:
        logit("issue occured while trying to instantiate configparser")
        return
    config.read(nodeconf)
    changed = False

    # no DEFAULT in etc/node.conf
    for o in copy.copy(config.defaults()):
        logit("removing DEFAULT in node.conf")
        config.remove_option('DEFAULT', o)
        changed = True

    # sync section goes to etc/.node.conf
    if config.has_section('sync'):
        logit("removing sync in node.conf")
        config.remove_section('sync')
        changed = True

    for s in config.sections():
        for o in config.options(s):
            if o in ['sync_interval', 'push_interval', 'comp_check_interval']:
                logit("looping %s"%o)
                v = config.getint(s, o)
                config.remove_option(s, o)
                config.set(s, 'interval', v)
                changed = True
            if o in ['sync_days', 'push_days', 'comp_check_days']:
                logit("looping %s"%o)
                v = config.get(s, o)
                config.remove_option(s, o)
                config.set(s, 'days', v)
                changed = True
            if o in ['sync_period', 'push_period', 'comp_check_period']:
                logit("looping %s"%o)
                v = config.get(s, o)
                config.remove_option(s, o)
                config.set(s, 'period', v)
                changed = True

    if changed:
        logit("writing new node.conf")
        try:
            fp = open(nodeconf, 'w')
            config.write(fp)
            fp.close()
        except:
            logit("failed to write new %s"%nodeconf,stderr=True)

def save_exc():
    logit("begin")
    import traceback
    try:
        import tempfile

        try:
            import datetime
            now = str(datetime.datetime.now()).replace(' ', '-')
        except:
            now =""

        try:
            f = tempfile.NamedTemporaryFile(dir=pathtmp, prefix='exc-'+now+'-')
        except:
            return
        f.close()
        f = open(f.name, 'w')
        traceback.print_exc(file=f)
        logit("unexpected error. stack saved in %s"%f.name,stderr=True)
        f.close()
    except:
        logit("unexpected error",stderr=True)
        traceback.print_exc()

def purge_collector_api_cache():
    logit("begin")
    fname = os.path.join(pathvar, "collector")
    if os.path.exists(fname) and os.path.isfile(fname):
        logit("unlink file %s"%fname)
        os.unlink(fname)

def chmod_directories():
    logit("begin")
    if not hasattr(os, "walk"):
        logit("os.walk not available")
        return
    if sysname == 'Windows':
        logit("skip : unsupported on Windows")
        return
    for d in (pathbin, pathlib, pathusr):
        if d is None:
            continue
        for dirname, dirnames, filenames in os.walk(d):
            for subdirname in dirnames:
                dirpath = os.path.join(dirname, subdirname)
                try:
                    os.chmod(dirpath, p0755)
                    msg = "setting %s permissions to 0755" % dirpath
                except:
                    msg = "issue met while trying to set %s permissions to 0755" % dirpath
                logit(msg)

def log_file_info(path):
    try:
        info = os.lstat(path)
    except:
        msg = "issue met while trying to get [%s] os.lstat information" % path
        logit(msg)
    return
    string = "uid[%d] gid[%d] perms[%s] file[%s]" % (info.st_uid, info.st_gid, oct(info.st_mode & 777), path)
    logit(string)

def clean_pyc():
    logit("begin")
    for fpath in glob.glob(os.path.join(pathlib, "*.pyc")) + glob.glob(os.path.join(pathlib, "__pycache__/*.pyc")):
        try:
            os.remove(fpath)
        except:
            msg = "issue met while trying to remove pyc file [%s]" % fpath
            logit(msg)
            pass

def dump_install_content():
    logit("begin")
    if sysname == 'Windows':
        logit("skip : unsupported on Windows")
        return
    if not hasattr(os, "walk"):
        logit("os.walk not available")
        return
    for d in (pathbin, pathlib, pathusr):
        if d is None:
            continue
        for dirname, dirnames, filenames in os.walk(d):
            for subdirname in dirnames:
                dirpath = os.path.join(dirname, subdirname)
                log_file_info(dirpath)
            for filename in filenames:
                filepath = os.path.join(dirname, filename)
                log_file_info(filepath)

def convert_to_lsb():
    logit("begin")
    if sysname == 'Windows':
        logit("skip : unsupported on Windows")
        return
    if len(glob.glob(pathetc+"/*")) > 0:
        logit("skip : skip convert to lsb because /etc/opensvc/ is not empty")
        return
    if not os.path.exists("/opt/opensvc"):
        logit("skip : skip convert to lsb because /opt/opensvc/ does not exist")
        return
    for p in glob.glob("/opt/opensvc/etc/*conf") + glob.glob("/opt/opensvc/etc/sssu") + glob.glob("/opt/opensvc/etc/*pem") + glob.glob("/opt/opensvc/etc/*pub"):
        logit("migrate " + p)
        shutil.copy(p, pathetc)
    for p in glob.glob("/opt/opensvc/etc/*.env"):
        logit("migrate " + p)
        svcname = os.path.basename(p)[:-4]
        shutil.copy(os.path.realpath(p), pathetc)
        os.symlink("/usr/bin/svcmgr", os.path.join(pathetc, svcname))
    for p in glob.glob("/opt/opensvc/etc/*.d") + glob.glob("/opt/opensvc/etc/*.dir"):
        logit("migrate " + p)
        if os.path.islink(p):
            bp = os.path.basename(p)
            linkto = os.readlink(p)
            if linkto.startswith("/opt/opensvc/etc"):
                linkto.replace("/opt/opensvc/etc/", "")
            dst = os.path.join(pathetc, bp)
            os.symlink(linkto, dst)
        elif os.path.isdir(p):
            bp = os.path.basename(p)
            dst = os.path.join(pathetc, bp)
            shutil.copytree(p, dst, symlinks=True)
        else:
            shutil.copy(p, pathetc)
    for p in glob.glob("/opt/opensvc/var/*"):
        if os.path.basename(p) == "btrfs":
            continue
        logit("migrate " + p)
        bp = os.path.basename(p)
        dst = os.path.join(pathvar, bp)
        if os.path.exists(dst):
            continue
        if os.path.isdir(p):
            try:
                shutil.copytree(p, dst, symlinks=True)
            except:
                # best effort for var
                pass
        else:
            shutil.copy(p, pathvar)

def restart_daemon():
    logit("begin")
    flag = os.path.join(pathvar, "postinstall.restart")
    if not os.path.exists(flag):
        return
    os.unlink(flag)
    os.environ['OPENSVC_AGENT_UPGRADE']="1"
    cmd = "%s/nodemgr daemon start" % pathbin
    logit(cmd, stdout=True)
    os.system(cmd)
    cmd = "%s/nodemgr daemon running" % pathbin
    os.system(cmd)
    del os.environ['OPENSVC_AGENT_UPGRADE']

def init_secret():
    logit("begin")
    time.sleep(1)
    if sysname == 'Windows':
        cmd = "nodemgr ping --node 127.0.0.1"
    else:
        cmd =  "%s/nodemgr ping --node 127.0.0.1" % pathbin
    os.system(cmd)

def solaris_fmri_setprop():
    logit("begin")
    if sysname != 'SunOS':
        logit("Only useful on SunOS",stdout=False)
        return
    else:
        logit("issuing setprop on fmri",stdout=False)
        osvcfmri = 'svc:/site/application/opensvc:boot'
        cmd = ['svccfg', '-s', osvcfmri, 'setprop', 'boot/firstrun', '=', 'done', '2>/dev/null']
        ret = os.system(' '.join(cmd))
        if ret == 0:
	    # setprop ok, Solaris install from IPS package with standard FMRI
            logit("setprop ok. now refreshing fmri",stdout=False)
	    cmd = ['svccfg', '-s', osvcfmri, 'refresh']
            ret = os.system(' '.join(cmd))
	    if ret == 0:
	        logit("fmri refresh successfully executed",stdout=False)

try:
    move_var_files_in_subdirs()
    move_usr_to_opt()
    missing_dirs()
    convert_svclinks()
    install_cron()
    install_rc()
    gen_keys()
    install_profile()
    install_etc_path()
    install_bash_completion()
    move_host_mode()
    install_pythonlink()
    nodeconf_params()
    purge_collector_api_cache()
    chmod_directories()
    convert_to_lsb()
    move_env_to_conf()
    move_var_files_19()
    clean_pyc()
    dump_install_content()
    restart_daemon()
    init_secret()
    solaris_fmri_setprop()
    logit("\nOpenSVC postinstall terminated\n",stdout=True)
except:
    save_exc()
    sys.exit(1)
   0707010005c884000081fd0000000000000000000000015ab21754000004bb000000660000000000000000000000000000002600000000root/usr/share/opensvc/bin/preinstall #!/bin/sh

# set OSVC_ROOT_PATH
test -f /etc/sysconfig/opensvc && . /etc/sysconfig/opensvc
test -f /etc/default/opensvc && . /etc/default/opensvc
test -f /etc/defaults/opensvc && . /etc/defaults/opensvc

if [ -z "$OSVC_ROOT_PATH" ]
then
	FLAG=/var/lib/opensvc/postinstall.restart
else
	FLAG=$OSVC_ROOT_PATH/var/postinstall.restart
	PATH=$PATH:$OSVC_ROOT_PATH/bin
	export PATH
fi

# create the flag hosting dir if it does not exist yet
mkdir -p `dirname $FLAG` >/dev/null 2>&1

# clean up lingering restart flag
rm -f $FLAG

# opensvc was not installed yet. just set the daemon start flag 
which nodemgr >/dev/null 2>&1 || {
	touch $FLAG
	exit 0
}

nodemgr daemon running >/dev/null 2>&1
RUNNING=$?

# option not found => upgrade from 1.8-
if [ $RUNNING -eq 2 ]
then
	echo "daemon flagged for start"
	touch $FLAG

	echo "freeze all services"
	svcmgr freeze >/dev/null 2>&1
        # can output error if no services are present
	exit 0
fi

# set a flag for postinstall to know if the daemon should be restarted
if [ $RUNNING -eq 0 ]
then
	echo "daemon flagged for restart"
	touch $FLAG

        echo "stop the daemon, leaving the node in agent upgrade mode"
        OPENSVC_AGENT_UPGRADE=1 nodemgr daemon stop
fi
 0707010005c885000081fd0000000000000000000000015ab2175400000193000000660000000000000000000000000000002800000000root/usr/share/opensvc/bin/preuninstall   #!/bin/sh

ARGS=$1

if [ `uname` -eq 'SunOS' ]
then
	ARGS=0
fi

if [ -z "$OSVC_ROOT_PATH" ]
then
	FLAG=/var/lib/opensvc/postinstall.restart
else
	FLAG=$OSVC_ROOT_PATH/var/postinstall.restart
	PATH=$PATH:$OSVC_ROOT_PATH/bin
	export PATH
fi

nodemgr daemon running >/dev/null 2>&1
RUNNING=$?

if [ $ARGS -eq 0 ]
then
	# uninstall package
	if [ $RUNNING -eq 0 ]
	then
		nodemgr daemon stop
	fi
	exit 0
fi

 0707010005c886000041ed0000000000000000000000045ab2183a00000000000000660000000000000000000000000000001b00000000root/usr/share/opensvc/lib    0707010005c887000081b40000000000000000000000015ab2175400000377000000660000000000000000000000000000003200000000root/usr/share/opensvc/lib/checkAdvfsUsageOSF1.py import checks
import glob
import re
import os
from rcUtilities import justcall
import rcAdvfs

class check(checks.check):
    def __init__(self, svcs=[]):
        checks.check.__init__(self, svcs)

    chk_type = "fs_u"

    def find_svc(self, name):
        for svc in self.svcs:
            for resource in svc.get_resources('pool'):
                if resource.poolname == name:
                    return svc.svcname
        return ''

    def do_check(self):
        o = rcAdvfs.Fdmns()
        r = []
        for dom in o.list_fdmns():
            try:
                d = o.get_fdmn(dom)
                r.append({
                          'chk_instance': dom,
                          'chk_value': str(d.used_pct),
                          'chk_svcname': self.find_svc(dom),
                         })
            except rcAdvfs.ExInit:
                pass
        return r
 0707010005c888000081b40000000000000000000000015ab21754000009c9000000660000000000000000000000000000003600000000root/usr/share/opensvc/lib/checkBtrfsDevStatsLinux.py import checks
from rcUtilities import justcall
import os
import re
from rcGlobalEnv import rcEnv

class check(checks.check):
    """
# btrfs dev stats /mnt
[/dev/loop0].write_io_errs   0
[/dev/loop0].read_io_errs    0
[/dev/loop0].flush_io_errs   0
[/dev/loop0].corruption_errs 0
[/dev/loop0].generation_errs 0
[/dev/loop1].write_io_errs   0
[/dev/loop1].read_io_errs    0
[/dev/loop1].flush_io_errs   0
[/dev/loop1].corruption_errs 0
[/dev/loop1].generation_errs 0
    """
    chk_type = "btrfs"

    def _get_dev_stats(self, mntpt, data):
        cmd = ['btrfs', 'dev', 'stats', mntpt]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return data
        for line in out.split('\n'):
            l = line.split()
            if len(l) != 2:
                continue
            key, val = l
            l = key.split('.')
            if len(l) != 2:
                continue
            dev, err_type = l
            dev = dev.lstrip('[').rstrip(']')
            if dev not in data:
                data[dev] = {}
            data[dev][err_type] = val
        return data

    def get_dev_stats(self):
        mntpts = self.get_btrfs_mounts()
        data = {}
        if mntpts is None:
            return data
        for mntpt in mntpts:
            data = self._get_dev_stats(mntpt, data)
        return data

    def get_btrfs_mounts(self):
        mntpts = []
        p = '/proc/mounts'
        if not os.path.exists(p):
            return
        with open(p, 'r') as f:
            buff = f.read()
        for line in buff.split('\n'):
            if 'btrfs' not in line:
                continue
            l = line.split()
            if len(l) < 2:
                continue
            mntpt = l[1]
            if not os.path.exists(mntpt):
                continue
            mntpts.append(mntpt)
        return mntpts

    def find_svc(self, dev):
        for svc in self.svcs:
            if dev in svc.sub_devs():
                return svc.svcname
        return ''

    def do_check(self):
        r = []
        data = self.get_dev_stats()
        if data is None:
            return r
        for dev, _data in data.items():
            for err_type, val in _data.items():
                r.append({'chk_instance': dev+'.'+err_type,
                          'chk_value': val,
                          'chk_svcname': self.find_svc(dev),
                        })
        return r

if __name__ == "__main__":
    o = check()
    r = o.do_check()
    print(r)
   0707010005c889000081b40000000000000000000000015ab2175400000aeb000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/checkEthHP-UX.py   import checks
import os
from rcUtilities import justcall, which
from rcGlobalEnv import rcEnv
import glob
import rcEthtool

class check(checks.check):
    chk_type = "eth"

    def do_check(self):
        cmd = ["lanscan", "-q"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return self.undef
        r = []
        intf = set()
        for line in out.split("\n"):
            if len(line) == 0:
                continue
            l = line.split()
            n = len(l)
            if n == 1:
                # add interfaces with an inet config
                if self.has_inet(l[0]):
                    intf.add(l[0])
            elif n > 1:
                # add slaves for apa with an inet config
                if self.has_inet(l[0]):
                    for w in l[1:]:
                        intf.add(w)
            else:
                continue

        for i in intf:
            r += self.do_check_intf(i)

        return r

    def has_inet(self, intf):
        cmd = ["ifconfig", "lan"+intf]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return False
        if 'inet' in out:
            return True
        return False

    def do_check_intf(self, intf):
        r = []
        cmd = ["lanadmin", "-x", intf]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return []

        intf = "lan"+intf
        inst = intf + ".link"
        if "link is down" in out:
            val = "0"
        else:
            val = "1"
        r.append({
                  'chk_instance': inst,
                  'chk_value': val,
                  'chk_svcname': '',
                 })

        inst = intf + ".speed"
        val = "0"
        for line in out.split('\n'):
            if "Speed" not in line:
                continue
            try:
                val = line.split()[2]
            except:
                pass
        r.append({
                  'chk_instance': inst,
                  'chk_value': val,
                  'chk_svcname': '',
                 })

        inst = intf + ".autoneg"
        val = "0"
        for line in out.split('\n'):
            if "Autoneg" not in line:
                continue
            if " On":
                val = "1"
        r.append({
                  'chk_instance': inst,
                  'chk_value': val,
                  'chk_svcname': '',
                 })

        inst = intf + ".duplex"
        val = '0'
        for line in out.split('\n'):
            if "Speed" not in line:
                continue
            if 'Full-Duplex' in line:
                val = "1"
        r.append({
                  'chk_instance': inst,
                  'chk_value': val,
                  'chk_svcname': '',
                 })

        return r
 0707010005c88a000081b40000000000000000000000015ab2175400000b20000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/checkEthLinux.py   import checks
import os
from rcUtilities import justcall, which
from rcGlobalEnv import rcEnv
import glob
import rcEthtool

class check(checks.check):
    chk_type = "eth"
    bonding_p = '/proc/net/bonding'

    def get_intf(self):
        intf = []
        l = glob.glob(self.bonding_p+'/*')
        for bond in l:
            intf += self.add_slaves(bond)
        rcIfconfig = __import__("rcIfconfig"+rcEnv.sysname)
        ifconfig = rcIfconfig.ifconfig()
        for i in ifconfig.intf:
            if not i.name.startswith('eth'):
                continue
            if len(i.ipaddr) == 0 and len(i.ip6addr) == 0:
                continue
            if i.name in intf:
                continue
            intf.append(i.name)
        return intf

    def add_slaves(self, bond):
        intf = []
        try:
            f = open(bond, 'r')
            buff = f.read()
            f.close()
        except:
            return intf
        for line in buff.split('\n'):
            if line.startswith('Slave Interface:'):
                intf.append(line.split()[-1])
        return intf

    def do_check(self):
        l = self.get_intf()
        if len(l) == 0:
            return self.undef
        r = []
        for intf in l:
            r += self.do_check_intf(intf)
        return r

    def do_check_intf(self, intf):
        r = []
        try:
            ethtool = rcEthtool.Ethtool(intf)
            ethtool.load()
        except rcEthtool.LoadError:
            return []

        inst = intf + ".speed"
        val = ethtool.speed
        if val is not None:
            val.replace('Mb/s', '')
            r.append({
                  'chk_instance': inst,
                  'chk_value': val,
                  'chk_svcname': '',
                 })

        inst = intf + ".autoneg"
        if val is not None:
            val = ethtool.auto_negotiation
            if val == 'on':
                val = '1'
            else:
                val = '0'
            r.append({
                  'chk_instance': inst,
                  'chk_value': val,
                  'chk_svcname': '',
                 })

        inst = intf + ".duplex"
        val = ethtool.duplex
        if val is not None:
            if val == 'Full':
                val = '1'
            else:
                val = '0'
            r.append({
                  'chk_instance': inst,
                  'chk_value': val,
                  'chk_svcname': '',
                 })

        inst = intf + ".link"
        val = ethtool.link_detected
        if val is not None:
            if val == 'yes':
                val = '1'
            else:
                val = '0'
            r.append({
                  'chk_instance': inst,
                  'chk_value': val,
                  'chk_svcname': '',
                 })

        return r
0707010005c88b000081b40000000000000000000000015ab21754000027c6000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/checkEthSunOS.py   import checks
import os
import re
from rcUtilities import justcall
from rcUtilitiesSunOS import get_solaris_version
from rcGlobalEnv import rcEnv

"""
# ifconfig -a
lo0: flags=2001000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv4,VIRTUAL> mtu 8232 index 1
        inet 127.0.0.1 netmask ff000000
lo0:1: flags=2001000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv4,VIRTUAL> mtu 8232 index 1
        zone frcp00vpd0385
        inet 127.0.0.1 netmask ff000000
lo0:2: flags=2001000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv4,VIRTUAL> mtu 8232 index 1
        zone frcp00vpd0388
        inet 127.0.0.1 netmask ff000000
lo0:3: flags=2001000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv4,VIRTUAL> mtu 8232 index 1
        zone frcp00vpd0192
        inet 127.0.0.1 netmask ff000000
lo0:4: flags=2001000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv4,VIRTUAL> mtu 8232 index 1
        zone frcp00vpd0192
        inet 128.1.1.192 netmask ffff0000
lo0:5: flags=2001000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv4,VIRTUAL> mtu 8232 index 1
        zone frcp00vpd0179
        inet 127.0.0.1 netmask ff000000
aggr1: flags=1000843<UP,BROADCAST,RUNNING,MULTICAST,IPv4> mtu 1500 index 2
        inet 172.31.4.195 netmask ffffff00 broadcast 172.31.4.255
        ether 0:15:17:bb:85:58
aggr1:1: flags=1000843<UP,BROADCAST,RUNNING,MULTICAST,IPv4> mtu 1500 index 2
        zone frcp00vpd0385
        inet 172.31.4.180 netmask ffffff00 broadcast 172.31.4.255
aggr1:2: flags=1000843<UP,BROADCAST,RUNNING,MULTICAST,IPv4> mtu 1500 index 2
        zone frcp00vpd0388
        inet 172.31.4.183 netmask ffffff00 broadcast 172.31.4.255
aggr1:3: flags=1000843<UP,BROADCAST,RUNNING,MULTICAST,IPv4> mtu 1500 index 2
        zone frcp00vpd0179
        inet 172.31.4.67 netmask ffffff00 broadcast 172.31.4.255
aggr2: flags=1000843<UP,BROADCAST,RUNNING,MULTICAST,IPv4> mtu 1500 index 5
        inet 172.31.195.195 netmask ffffff00 broadcast 172.31.195.255
        ether 0:15:17:bb:85:59
bnx3: flags=1000843<UP,BROADCAST,RUNNING,MULTICAST,IPv4> mtu 1500 index 4
        inet 55.16.201.195 netmask fffffc00 broadcast 55.16.203.255
        ether 0:24:e8:35:9d:dd
bnx3:1: flags=1000843<UP,BROADCAST,RUNNING,MULTICAST,IPv4> mtu 1500 index 4
        zone frcp00vpd0385
        inet 55.16.201.142 netmask fffffc00 broadcast 55.16.203.255
bnx3:2: flags=1000843<UP,BROADCAST,RUNNING,MULTICAST,IPv4> mtu 1500 index 4
        zone frcp00vpd0388
        inet 55.16.201.145 netmask fffffc00 broadcast 55.16.203.255
bnx3:3: flags=1000843<UP,BROADCAST,RUNNING,MULTICAST,IPv4> mtu 1500 index 4
        zone frcp00vpd0179
        inet 55.16.202.98 netmask fffffc00 broadcast 55.16.203.255

Solaris 10
==========
# ndd -get /dev/bnx3 link_speed
1000
# ndd -get /dev/bnx3 link_duplex
1
# ndd -get /dev/bnx3 link_status
1
kstat -p | grep link_ | grep  ce:0:ce0:link
ce:0:ce0:link_asmpause  0
ce:0:ce0:link_duplex    2
ce:0:ce0:link_pause     0
ce:0:ce0:link_speed     1000
ce:0:ce0:link_up        1

Solaris 11
==========
# dladm show-link -p -o link,class,over l226g0
l226g0:vnic:aggr0
# dladm show-link -p -o link,class,over aggr0
aggr0:aggr:net0 net2
# dladm show-link -p -o link,class,over net0
net0:phys:
# dladm show-phys -p -o state,speed,duplex net0
up:1000:full
# dladm show-link -p -o link,class,over net2
# dladm show-phys -p -o state,speed,duplex net2
up:1000:full
"""

class check(checks.check):
    chk_type = "eth"
    kstat = None

    def _findphys(self, netif):
        res = ""
        cmd = ['/usr/sbin/dladm', 'show-link', '-p', '-o', 'link,class,over', netif]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return ""
        for line in out.splitlines():
            if len(line) == 0:
                break
            v = line.split(':')
            if v[1] == 'phys':
                self.l[self.topif].add(v[0])
            else:
                ifs = v[2].split(' ')
                for i in ifs:
                    res = self._findphys(i)
            return "OK"

    def do_check(self):
        self.osver = get_solaris_version()
        self.ifs = []
        cmd = ['/usr/sbin/ifconfig', '-a']
        out, err, ret = justcall(cmd)
        if ret != 0:
            return self.undef
        lines = out.split('\n')
        if len(lines) == 0:
            return self.undef
        for line in lines:
            if line.startswith(' '):
                continue
            if line.startswith('lo'):
                continue
            if line.startswith('sppp'):
                continue
            if self.osver < 11:
                if line.startswith('aggr'):
                    continue
            if 'index' not in line:
                continue
            l = line.split(':')
            if 'index' not in l[1]:
                continue
            if len(l[0]) < 3:
                continue
            if l[0] in self.ifs:
                continue
            else:
                self.ifs.append(l[0])
        if self.osver >= 11:
            self.l = {}
            for ifn in self.ifs:
                if ifn not in self.l:
                    self.l[ifn] = set()
                    self.topif = ifn
                    ret = self._findphys(ifn)
            cmd = ['/usr/sbin/dladm', 'show-phys', '-p', '-o', 'link,state,speed,duplex,device']
            out, err, ret = justcall(cmd)
            if ret != 0:
                return self.undef
            """
            public0:up:1000:full:bnx0
            """
            self.phys = {}
            if len(lines) == 0:
                return self.undef
            for line in out.splitlines():
                l = line.split(':')
                if len(l) != 5:
                    continue
                self.phys[l[0]] = {
                    "link": l[0],
                    "state": l[1],
                    "speed": l[2],
                    "duplex": l[3],
                    "device": l[4],
                }

        r = []
        r += self.do_check_speed()
        r += self.do_check_duplex()
        r += self.do_check_link()
        return r

    def do_check_speed(self):
        r = []
        if self.osver >= 11:
            for ifn in self.ifs:
                for phy in self.l[ifn]:
                    if phy in self.phys:
                        r.append({
                            'chk_instance': '%s.%s.speed' % (ifn, self.phys[phy]["device"]),
                            'chk_value': str(self.phys[phy]["speed"]),
                            'chk_svcname': '',
                        })
            return r
        for ifn in self.ifs:
            val = self.get_param(ifn, 'link_speed')
            r.append({
                      'chk_instance': '%s.speed'%ifn,
                      'chk_value': str(val),
                      'chk_svcname': '',
                     })
        return r

    def do_check_duplex(self):
        r = []
        if self.osver >= 11:
            for ifn in self.ifs:
                for phy in self.l[ifn]:
                    if phy in self.phys:
                        if self.phys[phy]["duplex"] == 'full':
                            val = "1"
                        else:
                            val = "0"
                        r.append({
                            'chk_instance': '%s.%s.duplex' % (ifn, self.phys[phy]["device"]),
                            'chk_value': val,
                            'chk_svcname': '',
                        })
            return r
        for ifn in self.ifs:
            val = self.get_param(ifn, 'link_duplex')
            r.append({
                      'chk_instance': '%s.duplex'%ifn,
                      'chk_value': str(val),
                      'chk_svcname': '',
                     })
        return r

    def do_check_link(self):
        r = []
        if self.osver >= 11:
            for ifn in self.ifs:
                for phy in self.l[ifn]:
                    if phy in self.phys:
                        if self.phys[phy]["state"] == 'up':
                            val = "1"
                        else:
                            val = "0"
                        r.append({
                            'chk_instance': '%s.%s.link' % (ifn, self.phys[phy]["device"]),
                            'chk_value': val,
                            'chk_svcname': '',
                        })
            return r
        for ifn in self.ifs:
            val = self.get_param(ifn, 'link_status')
            r.append({
                      'chk_instance': '%s.link'%ifn,
                      'chk_value': str(val),
                      'chk_svcname': '',
                     })
        return r

    def get_param(self, intf, param):
        val = self.get_from_ndd(intf, param)
        if val is None:
            val = self.get_from_kstat(intf, param)
        return val

    def get_from_ndd(self, intf, param):
        cmd = ['/usr/sbin/ndd', '-get', '/dev/'+intf, param]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return
        return out.strip()

    def get_from_kstat(self, intf, param):
        inst = re.sub(r"[a-zA-Z]+", "", intf)
        drv = re.sub(r"[0-9]+", "", intf)
        data = {
          "ce": {
            "link_status": ":"+intf+":link_up",
            "link_duplex": ":"+intf+":link_duplex",
            "link_speed": ":"+intf+":link_speed",
          },
          "nxge": {
            "link_status": ":mac:link_up",
            "link_duplex": ":mac:link_duplex",
            "link_speed": ":Port Stats:link_speed",
          },
        }

        if self.kstat is None:
            cmd = ['/usr/bin/kstat', '-p']
            out, err, ret = justcall(cmd)
            if ret == 0:
                self.kstat = out

        if self.kstat is None:
            return

        lines = self.kstat.split('\n')

        if len(lines) == 0:
            return

        prefix = ':'.join((drv, inst))
        if drv not in data:
            return
        _data = data[drv]
        if param not in _data:
            return
        _param = _data[param]
        patt = prefix + _param

        for line in lines:
            if not line.startswith(patt):
                continue
            l = line.split()
            return l[-1]
        return
  0707010005c88c000081b40000000000000000000000015ab2175400000419000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/checkFmFmadm.py    import checks
import os
from rcUtilities import justcall, which
from rcGlobalEnv import rcEnv

class check(checks.check):
    prefixes = [os.path.join(os.sep, "usr", "sbin")]
    fmadm = "fmadm"
    chk_type = "fm"
    chk_name = "Solaris fmadm"

    def find_fmadm(self):
        if which(self.fmadm):
            return self.fmadm
        for prefix in self.prefixes:
            fmadm = os.path.join(prefix, self.fmadm)
            if os.path.exists(fmadm):
                return fmadm
        return

    def do_check(self):
        r = self.do_check_ldpdinfo()
        return r

    def do_check_ldpdinfo(self):
        fmadm = self.find_fmadm()
        if fmadm is None:
            return self.undef
        os.chdir(rcEnv.paths.pathtmp)
        cmd = [fmadm, 'faulty']
        out, err, ret = justcall(cmd)
        if ret != 0:
            return self.undef
        r = []
        r.append({
              'chk_instance': 'faults ',
              'chk_value': str(len(out)),
              'chk_svcname': '',
            })
        return r
   0707010005c88d000081b40000000000000000000000015ab2175400000668000000660000000000000000000000000000003000000000root/usr/share/opensvc/lib/checkFmOpenManage.py   import checks
import os
from rcUtilities import justcall, which
from rcGlobalEnv import rcEnv

class check(checks.check):
    omreport = "/opt/dell/srvadmin/bin/omreport"
    chk_type = "om"
    chk_name = "OpenManage"

    def find_omreport(self):
        if which(self.omreport):
            return self.omreport
        return

    def do_check(self):
        r = self.do_check_system()
        r += self.do_check_chassis()
        return r

    def do_check_chassis(self):
        return self.do_check_gen("chassis")

    def do_check_system(self):
        return self.do_check_gen("system")

    def do_check_gen(self, command):
        omreport = self.find_omreport()
        if omreport is None:
            return self.undef
        cmd = [omreport, command]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return self.undef
        lines = out.split('\n')
        if len(lines) == 0:
            return self.undef
        r = []
        for line in lines:
            l = line.split(" : ")
            if len(l) != 2:
                continue
            inst = l[1].strip().lower()
            state = l[0].strip().lower()
            if state == "severity":
                continue
            elif state == "ok":
                state = 0
            else:
                state = 1
            r.append({
                      'chk_instance': inst,
                      'chk_value': str(state),
                      'chk_svcname': '',
                     })
        return r

if __name__ == "__main__":
    from rcUtilities import printplus
    o = check()
    tab = o.do_check()
    printplus(tab)

0707010005c88e000081b40000000000000000000000015ab21754000003e3000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/checkFsInodeAIX.py import checks
from rcUtilities import justcall

class check(checks.check):
    chk_type = "fs_i"

    def find_svc(self, mountpt):
        for svc in self.svcs:
            for resource in svc.get_resources('fs'):
                if resource.mount_point == mountpt:
                    return svc.svcname
        return ''

    def do_check(self):
        cmd = ['df', '-i']
        out, err, ret = justcall(cmd)
        if ret != 0:
            return self.undef
        lines = out.split('\n')
        if len(lines) < 2:
            return self.undef
        r = []
        for line in lines[1:]:
            l = line.split()
            if len(l) != 7:
                continue
            if l[1] == '-':
                continue
            if ":/" in l[0]:
                continue
            r.append({
                      'chk_instance': l[6],
                      'chk_value': l[5],
                      'chk_svcname': self.find_svc(l[6]),
                     })
        return r
 0707010005c88f000081b40000000000000000000000015ab21754000003bf000000660000000000000000000000000000003100000000root/usr/share/opensvc/lib/checkFsInodeDarwin.py  import checks
from rcUtilities import justcall

class check(checks.check):
    chk_type = "fs_i"

    def find_svc(self, mountpt):
        for svc in self.svcs:
            for resource in svc.get_resources('fs'):
                if resource.mount_point == mountpt:
                    return svc.svcname
        return ''

    def do_check(self):
        cmd = ['df', '-lPi']
        (out,err,ret) = justcall(cmd)
        if ret != 0:
            return self.undef
        lines = out.split('\n')
        if len(lines) < 2:
            return self.undef
        r = []
        for line in lines[1:]:
            l = line.split()
            if len(l) != 9:
                continue
            if l[5].startswith('/Volumes'):
                continue
            r.append({
                      'chk_instance': l[8],
                      'chk_value': l[7],
                      'chk_svcname': self.find_svc(l[8]),
                     })
        return r
 0707010005c890000081b40000000000000000000000015ab217540000055a000000660000000000000000000000000000003000000000root/usr/share/opensvc/lib/checkFsInodeHP-UX.py   import checks
from rcUtilities import call

class check(checks.check):
    chk_type = "fs_i"

    def find_svc(self, mountpt):
        for svc in self.svcs:
            for resource in svc.get_resources('fs'):
                if resource.mount_point == mountpt:
                    return svc.svcname
        return ''

    def do_check(self):
        """
        # bdf -li
        Filesystem          kbytes    used   avail %used  iused  ifree %iuse Mounted on
        /dev/vg00/lvol3    1048576  228160  814136   22%   2105  25607    8% /
        """
        cmd = ['bdf', '-li']
        (ret, out, err) = call(cmd)
        if ret != 0:
            return self.undef
        lines = out.split('\n')
        if len(lines) < 2:
            return self.undef
        r = []
        for line in lines[1:]:
            l = line.split()
            if len(l) == 9:
                inst = ' '.join(l[8:])
                r.append({'chk_instance': inst,
                      'chk_value': l[7],
                      'chk_svcname': self.find_svc(inst),
                     }
                    )
            elif len(l) == 8:
                inst = ' '.join(l[7:])
                r.append({'chk_instance': inst,
                      'chk_value': l[6],
                      'chk_svcname': self.find_svc(inst),
                     }
                    )
        return r
  0707010005c891000081b40000000000000000000000015ab21754000006f4000000660000000000000000000000000000003000000000root/usr/share/opensvc/lib/checkFsInodeLinux.py   import checks
from rcUtilities import justcall

class check(checks.check):
    chk_type = "fs_i"

    def find_svc(self, mountpt):
        for svc in self.svcs:
            for resource in svc.get_resources('fs'):
                if resource.mount_point == mountpt:
                    return svc.svcname
        return ''

    def do_check(self):
        cmd = ['df', '-lPi']
        (out,err,ret) = justcall(cmd)
        if ret != 0:
            return self.undef
        lines = out.split('\n')
        if len(lines) < 2:
            return self.undef
        r = []
        for line in lines[1:]:
            l = line.split()
            if len(l) != 6:
                continue
            # discard bind mounts: we get metric from the source anyway
            if l[0].startswith('/') and not l[0].startswith('/dev') and not l[0].startswith('//'):
                continue
            if l[5].startswith('/Volumes'):
                continue
            if l[5].startswith('/media/'):
                continue
            if l[5].startswith('/run'):
                continue
            if l[5].startswith('/sys/'):
                continue
            if l[5].endswith('/shm'):
                continue
            if "/graph/" in l[5]:
                continue
            if "/aufs/mnt/" in l[5]:
                continue
            if "osvc_sync_" in l[0]:
                # do not report osvc sync snapshots fs usage
                continue
            if l[4] == '-':
                # vfat, btrfs, ... have no inode counter in df -i
                continue
            r.append({
                      'chk_instance': l[5],
                      'chk_value': l[4],
                      'chk_svcname': self.find_svc(l[5]),
                     })
        return r
0707010005c892000081b40000000000000000000000015ab2175400000443000000660000000000000000000000000000003000000000root/usr/share/opensvc/lib/checkFsInodeSunOS.py   import checks
from rcUtilities import justcall

class check(checks.check):
    chk_type = "fs_i"

    def find_svc(self, mountpt):
        for svc in self.svcs:
            for resource in svc.get_resources('fs'):
                if resource.mount_point == mountpt:
                    return svc.svcname
        return ''

    def do_check(self):
        r = []
        for t in ['ufs', 'vxfs']:
            r += self._do_check(t)
        return r

    def _do_check(self, t):
        cmd = ['df', '-F', t, '-o', 'i']
        (out,err,ret) = justcall(cmd)
        if ret != 0:
            return self.undef
        lines = out.split('\n')
        if len(lines) < 2:
            return self.undef
        r = []
        for line in lines[1:]:
            l = line.split()
            if len(l) == 5:
                l = [''] + l
            elif len(l) != 6:
                continue
            r.append({
                      'chk_instance': l[5],
                      'chk_value': l[4],
                      'chk_svcname': self.find_svc(l[5]),
                     })
        return r
 0707010005c893000081b40000000000000000000000015ab21754000003e3000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/checkFsUsageAIX.py import checks
from rcUtilities import justcall

class check(checks.check):
    chk_type = "fs_u"

    def find_svc(self, mountpt):
        for svc in self.svcs:
            for resource in svc.get_resources('fs'):
                if resource.mount_point == mountpt:
                    return svc.svcname
        return ''

    def do_check(self):
        cmd = ['df', '-P']
        out, err, ret = justcall(cmd)
        if ret != 0:
            return self.undef
        lines = out.split('\n')
        if len(lines) < 2:
            return self.undef
        r = []
        for line in lines[1:]:
            l = line.split()
            if len(l) != 6:
                continue
            if l[1] == '-':
                continue
            if ":/" in l[0]:
                continue
            r.append({
                      'chk_instance': l[5],
                      'chk_value': l[4],
                      'chk_svcname': self.find_svc(l[5]),
                     })
        return r
 0707010005c894000081b40000000000000000000000015ab21754000005bc000000660000000000000000000000000000003100000000root/usr/share/opensvc/lib/checkFsUsageDarwin.py  import checks
from rcUtilities import justcall

class check(checks.check):
    chk_type = "fs_u"

    def find_svc(self, mountpt):
        for svc in self.svcs:
            for resource in svc.get_resources('fs'):
                if resource.mount_point == mountpt:
                    return svc.svcname
        return ''

    def do_check(self):
        cmd = ['df', '-lP']
        (out,err,ret) = justcall(cmd)
        if ret != 0:
            return self.undef
        lines = out.split('\n')
        if len(lines) < 2:
            return self.undef
        r = []
        for line in lines[1:]:
            l = line.split()
            if len(l) != 6:
                continue
            # discard bind mounts: we get metric from the source anyway
            if l[0].startswith('/') and not l[0].startswith('/dev') and not l[0].startswith('//'):
                continue
            if l[5].startswith('/Volumes'):
                continue
            if l[5].startswith('/run'):
                continue
            if l[5].startswith('/sys/'):
                continue
            if l[5] == "/dev/shm":
                continue
            if "osvc_sync_" in l[0]:
                # do not report osvc sync snapshots fs usage
                continue
            r.append({
                      'chk_instance': l[5],
                      'chk_value': l[4],
                      'chk_svcname': self.find_svc(l[5]),
                     })
        return r
0707010005c895000081b40000000000000000000000015ab21754000005bc000000660000000000000000000000000000003200000000root/usr/share/opensvc/lib/checkFsUsageFreeBSD.py import checks
from rcUtilities import justcall

class check(checks.check):
    chk_type = "fs_u"

    def find_svc(self, mountpt):
        for svc in self.svcs:
            for resource in svc.get_resources('fs'):
                if resource.mount_point == mountpt:
                    return svc.svcname
        return ''

    def do_check(self):
        cmd = ['df', '-lP']
        (out,err,ret) = justcall(cmd)
        if ret != 0:
            return self.undef
        lines = out.split('\n')
        if len(lines) < 2:
            return self.undef
        r = []
        for line in lines[1:]:
            l = line.split()
            if len(l) != 6:
                continue
            # discard bind mounts: we get metric from the source anyway
            if l[0].startswith('/') and not l[0].startswith('/dev') and not l[0].startswith('//'):
                continue
            if l[5].startswith('/Volumes'):
                continue
            if l[5].startswith('/run'):
                continue
            if l[5].startswith('/sys/'):
                continue
            if l[5] == "/dev/shm":
                continue
            if "osvc_sync_" in l[0]:
                # do not report osvc sync snapshots fs usage
                continue
            r.append({
                      'chk_instance': l[5],
                      'chk_value': l[4],
                      'chk_svcname': self.find_svc(l[5]),
                     })
        return r
0707010005c896000081b40000000000000000000000015ab21754000005bc000000660000000000000000000000000000003000000000root/usr/share/opensvc/lib/checkFsUsageHP-UX.py   import checks
from rcUtilities import justcall

class check(checks.check):
    chk_type = "fs_u"

    def find_svc(self, mountpt):
        for svc in self.svcs:
            for resource in svc.get_resources('fs'):
                if resource.mount_point == mountpt:
                    return svc.svcname
        return ''

    def do_check(self):
        cmd = ['df', '-lP']
        (out,err,ret) = justcall(cmd)
        if ret != 0:
            return self.undef
        lines = out.split('\n')
        if len(lines) < 2:
            return self.undef
        r = []
        for line in lines[1:]:
            l = line.split()
            if len(l) != 6:
                continue
            # discard bind mounts: we get metric from the source anyway
            if l[0].startswith('/') and not l[0].startswith('/dev') and not l[0].startswith('//'):
                continue
            if l[5].startswith('/Volumes'):
                continue
            if l[5].startswith('/run'):
                continue
            if l[5].startswith('/sys/'):
                continue
            if l[5] == "/dev/shm":
                continue
            if "osvc_sync_" in l[0]:
                # do not report osvc sync snapshots fs usage
                continue
            r.append({
                      'chk_instance': l[5],
                      'chk_value': l[4],
                      'chk_svcname': self.find_svc(l[5]),
                     })
        return r
0707010005c897000081b40000000000000000000000015ab217540000080e000000660000000000000000000000000000003000000000root/usr/share/opensvc/lib/checkFsUsageLinux.py   import checks
from rcUtilities import justcall

class check(checks.check):
    chk_type = "fs_u"

    def find_svc(self, mountpt):
        for svc in self.svcs:
            for resource in svc.get_resources('fs'):
                if resource.mount_point == mountpt:
                    return svc.svcname
        return ''

    def do_check(self):
        cmd = ['df', '-lP']
        (out,err,ret) = justcall(cmd)
        if ret != 0:
            return self.undef
        lines = out.split('\n')
        if len(lines) < 2:
            return self.undef
        r = []
        for line in lines[1:]:
            l = line.split()
            if len(l) != 6:
                continue
            # discard bind mounts: we get metric from the source anyway
            if l[0].startswith('/') and not l[0].startswith('/dev') and not l[0].startswith('//'):
                continue
            if l[5].startswith('/Volumes'):
                continue
            if l[5].startswith('/media/'):
                continue
            if l[5].startswith('/run'):
                continue
            if l[5].startswith('/sys/'):
                continue
            if l[5].endswith('/shm'):
                continue
            if "/graph/" in l[5]:
                continue
            if "/aufs/mnt/" in l[5]:
                continue
            if "osvc_sync_" in l[0]:
                # do not report osvc sync snapshots fs usage
                continue
            svcname = self.find_svc(l[5])
            r.append({
                      'chk_instance': l[5],
                      'chk_value': l[4],
                      'chk_svcname': svcname,
                     })
            r.append({
                      'chk_instance': l[5]+".free",
                      'chk_value': l[3],
                      'chk_svcname': svcname,
                     })
            r.append({
                      'chk_instance': l[5]+".size",
                      'chk_value': l[1],
                      'chk_svcname': svcname,
                     })
        return r
  0707010005c898000081b40000000000000000000000015ab21754000005d0000000660000000000000000000000000000003000000000root/usr/share/opensvc/lib/checkFsUsageSunOS.py   import checks
from rcUtilities import justcall

class check(checks.check):
    chk_type = "fs_u"

    def find_svc(self, mountpt):
        for svc in self.svcs:
            for resource in svc.get_resources('fs'):
                if resource.mount_point == mountpt:
                    return svc.svcname
        return ''

    def do_check(self):
        r = []
        for t in ['ufs', 'vxfs']:
            r += self._do_check(t)
        return r

    def _do_check(self, t):
        cmd = ['df', '-F', t, '-k']
        (out,err,ret) = justcall(cmd)
        if ret != 0:
            return self.undef
        lines = out.split('\n')
        if len(lines) < 2:
            return self.undef
        r = []
        for line in lines[1:]:
            l = line.split()
            if len(l) == 5:
                l = [''] + l
            elif len(l) != 6:
                continue
            svcname = self.find_svc(l[5])
            r.append({
                      'chk_instance': l[5],
                      'chk_value': l[4],
                      'chk_svcname': svcname,
                     })
            r.append({
                      'chk_instance': l[5]+".free",
                      'chk_value': l[3],
                      'chk_svcname': svcname,
                     })
            r.append({
                      'chk_instance': l[5]+".size",
                      'chk_value': l[1],
                      'chk_svcname': svcname,
                     })
        return r
0707010005c899000081b40000000000000000000000015ab21754000003b0000000660000000000000000000000000000003200000000root/usr/share/opensvc/lib/checkFsUsageWindows.py import checks
from rcUtilitiesWindows import get_drives

class check(checks.check):
    chk_type = "fs_u"

    def find_svc(self, mountpt):
        for svc in self.svcs:
            for resource in svc.get_resources('fs'):
                if resource.mount_point == mountpt:
                    return svc.svcname
        return ''

    def do_check(self):
        import win32api
        cmd = ['df', '-lP']
        r = []
        for drive in get_drives():
            try:
                n_free_user, n_total, n_free = win32api.GetDiskFreeSpaceEx(drive+':\\')
            except:
                continue
            pct = 100 * (n_total - n_free) // n_total
            r.append({
                      'chk_instance': drive,
                      'chk_value': str(pct),
                      'chk_svcname': self.find_svc(drive),
                     })
        return r

if __name__ == "__main__":
    o = check()
    print(o.do_check())
0707010005c89a000081b40000000000000000000000015ab2175400000767000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/checkLagHP-UX.py   import checks
import os
from rcUtilities import justcall, which
from rcGlobalEnv import rcEnv
import glob
import rcEthtool

class check(checks.check):
    chk_type = "lag"

    def do_check(self):
        if not which("lanscan"):
            return []
        cmd = ["lanscan", "-q"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return self.undef
        r = []
        self.lag = {}
        for line in out.split("\n"):
            if len(line) == 0:
                continue
            l = line.split()
            n = len(l)
            if n < 2:
                # not apa
                continue
            if self.has_inet(l[0]):
                self.lag[l[0]] = l[1:]

        cmd = ["lanscan", "-v"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return self.undef

        self.intf_status = {}
        for line in out.split("\n"):
            if 'ETHER' not in line or line.startswith('0x'):
                continue
            l = line.split()
            n = len(l)
            if n < 5 or not l[3].startswith('lan'):
                continue
            intf = l[3].replace('lan', '')
            status = l[2]
            self.intf_status[intf] = status

        for intf, slaves in self.lag.items():
            i = 0
            for slave in slaves:
                if slave in self.intf_status and self.intf_status[slave] == 'UP':
                    i += 1
            inst = "lan" + intf + ".paths"
            val = str(i)
            r.append({
                  'chk_instance': inst,
                  'chk_value': val,
                  'chk_svcname': '',
                 })

        return r

    def has_inet(self, intf):
        cmd = ["ifconfig", "lan"+intf]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return False
        if 'inet' in out:
            return True
        return False
 0707010005c89b000081b40000000000000000000000015ab2175400000fdf000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/checkLagLinux.py   import checks
import os
import json
import glob
import datetime
from rcGlobalEnv import rcEnv
from rcIfconfigLinux import ifconfig

"""
Ethernet Channel Bonding Driver: v3.4.0 (October 7, 2008)

Bonding Mode: fault-tolerance (active-backup)
Primary Slave: None
Currently Active Slave: eth0
MII Status: up
MII Polling Interval (ms): 100
Up Delay (ms): 0
Down Delay (ms): 0

Slave Interface: eth0
MII Status: up
Link Failure Count: 0
Permanent HW addr: 00:23:7d:a0:20:fa

Slave Interface: eth1
MII Status: up
Link Failure Count: 0
Permanent HW addr: 00:23:7d:a0:20:f6

"""

class check(checks.check):
    chk_type = "lag"
    chk_name = "Linux network link aggregate"
    bonding_p = '/proc/net/bonding'

    def do_check(self):
        l = glob.glob(self.bonding_p+'/*')
        if len(l) == 0:
            return self.undef
        ifg = ifconfig()
        r = []
        for bond in l:
            ifname = os.path.basename(bond)
            intf = ifg.interface(ifname)
            if intf is None:
                continue
            if len(intf.ipaddr) + len(intf.ip6addr) == 0:
                continue
            r += self.do_check_bond(bond)
        return r

    def get_cache(self, bond, slave, uptime):
        cache_p = self.cache_path(bond, slave)
        try:
            with open(cache_p, 'r') as f:
                 buff = f.read()
            data = json.loads(buff)
            prev_uptime, prev_val = data
        except:
            prev_uptime, prev_val = 0, 0

        if prev_uptime >= uptime:
            # reboot
            prev_uptime, prev_val = 0, 0

        return prev_uptime, prev_val

    def uptime(self):
        with open('/proc/uptime', 'r') as f:
            uptime_seconds = float(f.readline().split()[0])
        return uptime_seconds

    def cache_path(self, bond, slave):
        cache_p = os.path.join(rcEnv.paths.pathtmp, "checkLagLinux.cache."+os.path.basename(bond)+"."+slave)
        return cache_p

    def write_cache(self, bond, slave, val, uptime):
        cache_p = self.cache_path(bond, slave)
        with open(cache_p, 'w') as f: f.write(json.dumps([uptime, val]))
        try:
            with open(cache_p, 'w') as f:
                f.write(json.dumps([uptime, val]))
        except:
            pass

    def do_check_bond(self, bond):
        r = []
        try:
            f = open(bond, 'r')
            buff = f.read()
            f.close()
        except:
            return r
        n_slave = 0
        lag = os.path.basename(bond)
        inst = lag
        for line in buff.split('\n'):
            if line.startswith('Slave Interface:'):
                n_slave += 1
                slave = line.split()[-1]
                inst = '.'.join((lag, slave))
            elif line.startswith('MII Status:'):
                val = line.split()[-1]
                if val == "up":
                    val = "0"
                else:
                    val = "1"
                r.append({
                          'chk_instance': inst+'.mii_status',
                          'chk_value': val,
                          'chk_svcname': '',
                         })
            elif line.startswith('Link Failure Count:'):
                val = int(line.split()[-1])
                uptime = self.uptime()
                prev_uptime, prev_val = self.get_cache(bond, slave, uptime)
                if uptime - prev_uptime > 3600:
                    # don't mask alerts by refreshing the cache too soon
                    self.write_cache(bond, slave, val, uptime)
                # Link Failure Count per hour
                val = 3600. * (val - prev_val) / (uptime - prev_uptime)
                r.append({
                          'chk_instance': inst+'.link_failure_per_hour',
                          'chk_value': "%.2f"%val,
                          'chk_svcname': '',
                         })
        r.append({
                  'chk_instance': lag+'.paths',
                  'chk_value': str(n_slave),
                  'chk_svcname': '',
                 })
        return r
 0707010005c89c000081b40000000000000000000000015ab2175400001ab9000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/checkLagSunOS.py   import checks
import os
from rcUtilities import justcall, which
from rcUtilitiesSunOS import get_solaris_version
from rcGlobalEnv import rcEnv

"""
Solaris 10
key: 1 (0x0001) policy: L4      address: 0:15:17:bb:82:d2 (auto)
           device       address                 speed           duplex  link    state
           e1000g0      0:15:17:bb:82:d2          1000  Mbps    full    up      attached
           bnx0         0:24:e8:35:61:3b          1000  Mbps    full    up      attached

Solaris 11
# dladm show-aggr
LINK              POLICY   ADDRPOLICY           LACPACTIVITY  LACPTIMER   FLAGS
aggr0             L4       auto                 off           short       -----
aggrbck0          L4       auto                 off           short       -----
aggrpriv0         L4       auto                 off           short       -----

# dladm show-phys
LINK              MEDIA                STATE      SPEED  DUPLEX    DEVICE
net4              Ethernet             up         10     full      usbecm0
net1              Ethernet             up         1000   full      ixgbe1
net0              Ethernet             up         1000   full      ixgbe0
net2              Ethernet             up         1000   full      ixgbe2
net3              Ethernet             up         1000   full      ixgbe3

# dladm show-link
LINK                CLASS     MTU    STATE    OVER
net4                phys      1500   up       --
net1                phys      1500   up       --
net2                phys      1500   up       --
net0                phys      1500   up       --
aggrbck0            aggr      1500   up       net1
aggrpriv0           aggr      1500   up       net3
bckg0               vnic      1500   up       aggrbck0
zrac1_a0            vnic      1500   up       aggr0
zrac3_p_a0          vnic      1500   up       aggrpriv0
"""

class check(checks.check):
    chk_type = "lag"
    chk_name = "Solaris network interface lag"

    def do_check(self):
        if not which("dladm"):
            return self.undef
        self.osver = get_solaris_version()
        if self.osver >= 11:
            cmd = ['dladm', 'show-phys', '-p', '-o', 'link,state,speed,duplex']
            out, err, ret = justcall(cmd)
            if ret != 0:
                return self.undef
            self.phys = out.split('\n')
            cmd = ['dladm', 'show-aggr', '-p', '-o', 'link']
        else:
            cmd = ['dladm', 'show-aggr']
        out, err, ret = justcall(cmd)
        if ret != 0:
            return self.undef
        if self.osver >= 11:
            self.aggs = out.split('\n')
            if len(self.aggs) == 0:
                return self.undef
            self.listaggs = {}
            cmd = ['dladm', 'show-link', '-p', '-o', 'link,over']
            out, err, ret = justcall(cmd)
            if ret != 0:
                return self.undef
            lines = out.split('\n')
            for line in lines:
                if len(line) == 0:
                    break
                l = line.split(':')
                if l[0] in self.aggs:
                    self.listaggs[l[0]] = l[1]
        else:
            self.lines = out.split('\n')
            if len(self.lines) == 0:
                return self.undef
        r = []
        r += self.do_check_speed()
        r += self.do_check_duplex()
        r += self.do_check_link()
        r += self.do_check_attach()
        return r

    def do_check_speed(self):
        r = []
        lag = ""
        i = 0
        if self.osver >= 11:
            for lag in self.aggs:
                if len(lag) == 0:
                    break
                nets = self.listaggs[lag].split(' ')
                for net in nets:
                    if len(net) == 0:
                        break
                    for phy in self.phys:
                        if phy.startswith(net+':'):
                            l = phy.split(':')
                            val = l[2]
                            r.append({
                                      'chk_instance': '%s.%s.speed'%(lag, net),
                                      'chk_value': str(val),
                                      'chk_svcname': '',
                                     })
            return r
        for line in self.lines:
            l = line.split()
            if len(l) < 4:
                continue
            elif line.startswith('key'):
                lag = l[1]
                i = 0
                continue
            elif l[0] == 'device':
                continue
            val = l[2]
            r.append({
                      'chk_instance': '%s.%d.speed'%(lag, i),
                      'chk_value': str(val),
                      'chk_svcname': '',
                     })
            i += 1
        return r

    def do_check_duplex(self):
        return self._do_check("duplex", "full", 4)

    def do_check_link(self):
        return self._do_check("link", "up", 5)

    def do_check_attach(self):
        return self._do_check("attach", "attached", 6)

    def _do_check(self, key, target, col):
        r = []
        lag = ""
        i = 0
        if self.osver >= 11:
            if key == "duplex":
                col = 3
            if key == "attach":
                return r
            if key == "link":
                col = 1
            for lag in self.aggs:
                if len(lag) == 0:
                    break
                nets = self.listaggs[lag].split(' ')
                for net in nets:
                    if len(net) == 0:
                        break
                    for phy in self.phys:
                        if phy.startswith(net+':'):
                            l = phy.split(':')
                            if l[col] != target:
                               val = 1
                            else:
                               val = 0
                            r.append({
                                      'chk_instance': '%s.%s.%s'%(lag, net, key),
                                      'chk_value': str(val),
                                      'chk_svcname': '',
                                     })
            return r
        for line in self.lines:
            l = line.split()
            if len(l) < col+1:
                continue
            elif line.startswith('key'):
                lag = l[1]
                i = 0
                continue
            elif l[0] == 'device':
                continue
            else:
                if l[col] != target:
                    val = 1
                else:
                    val = 0
                r.append({
                          'chk_instance': '%s.%d.%s'%(lag, i, key),
                          'chk_value': str(val),
                          'chk_svcname': '',
                         })
                i += 1
        return r
   0707010005c89d000081b40000000000000000000000015ab21754000002fb000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/checkLagWindows.py import checks
import os
import wmi

class check(checks.check):
    chk_type = "lag"
    chk_name = "Windows network link aggregate"

    def do_check(self):
        try:
            self.w = wmi.WMI(namespace="root\hpq")
        except:
            # no HP lag
            return []
        r = []
        for team in self.w.HP_EthernetTeam():
            r += self.do_check_team(team)
        return r

    def do_check_team(self, team):
        r = []
        inst = team.Description
        val = team.RedundancyStatus
        r.append({
                'chk_instance': inst+'.redundancy',
                'chk_value': str(val),
                'chk_svcname': '',
               })
        return r

if __name__ == "__main__":
    o = check()
    o.do_check()
 0707010005c89e000081b40000000000000000000000015ab21754000006d9000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/checkMceLinux.py   import checks
from rcUtilities import justcall, which
import os
from rcGlobalEnv import rcEnv
import datetime

class check(checks.check):
    chk_type = "mcelog"
    mcelog_p = "/var/log/mcelog"
    marker_p = os.path.join(rcEnv.paths.pathtmp, "checkMceLinunx.marker")

    def get_last_marker(self):
        try:
            f = open(self.marker_p, 'r')
        except:
            return None
        buff = f.read()
        f.close()
        return buff

    def gen_marker(self):
        return "opensvc marker " + str(datetime.datetime.now())+'\n'

    def update_marker(self):
        m = self.gen_marker()
        try:
            f = open(self.mcelog_p, "a")
            f.write(m)
            f.close()
        except:
            return
        try:
            f = open(self.marker_p, "w")
            f.write(m)
            f.close()
        except:
            return

    def do_check(self):
        if not os.path.exists(self.mcelog_p):
            return self.undef

        if not which("mcelog"):
            return self.undef

        try:
            f = open(self.mcelog_p, "r")
        except:
            return self.undef

        marker = self.get_last_marker()
        marker_found = False
        l = 0
        total = 0

        for line in f.readlines():
            total += 1
            if line == marker:
                marker_found = True
                continue
            if not marker_found:
                continue
            l += 1

        if not marker_found:
            l = total

        r = []
        r.append({
                  'chk_instance': "new lines",
                  'chk_value': str(l),
                  'chk_svcname': "",
                 })

        self.update_marker()
        return r
   0707010005c89f000081b40000000000000000000000015ab21754000008e0000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/checkMpathAIX.py   import checks
from rcUtilities import justcall

class check(checks.check):
    chk_type = "mpath"
    svcdevs = {}

    def find_svc(self, dev):
        for svc in self.svcs:
            if svc not in self.svcdevs:
                try:
                    devs = svc.sub_devs()
                except Exception as e:
                    devs = []
                self.svcdevs[svc] = devs
            if dev in self.svcdevs[svc]:
                return svc.svcname
        return ''

    def odmget(self, lname, attr):
        cmd = ['odmget', '-q', 'name='+lname+' AND attribute='+attr, 'CuAt']
        out, err, ret = justcall(cmd)
        for f in out.split('\n'):
            if "value" not in f:
                continue
            return f.split(" = ")[-1].strip('"')
        return None

    def disk_wwid(self, dev):
        return self.odmget(dev, 'wwid')

    def disk_id(self, dev, typ):
        if typ.startswith("vscsi"):
            return 'vscsi.'+dev
        else:
            return self.disk_wwid(dev)

    def do_check(self):
        cmd = ['lspath']
        out, err, ret = justcall(cmd)
        if ret != 0:
            return self.undef
        lines = out.split('\n')
        if len(lines) < 1:
            return self.undef
        r = []
        dev = None
        wwid = None
        for line in lines:
            l = line.split()
            if len(l) != 3:
                continue
            if l[0] != 'Enabled':
                continue
            if dev is None:
                dev = l[1]
                typ = l[2]
                wwid = self.disk_id(dev, typ)
                n = 1
            elif dev is not None and wwid is not None and dev != l[1]:
                r.append({'chk_instance': wwid,
                          'chk_value': str(n),
                          'chk_svcname': self.find_svc(dev),
                         })
                dev = l[1]
                typ = l[2]
                wwid = self.disk_id(dev, typ)
                n = 1
            else:
                n += 1
        if dev is not None and wwid is not None:
            r.append({'chk_instance': wwid,
                      'chk_value': str(n),
                      'chk_svcname': self.find_svc(dev),
                     })
        return r
0707010005c8a0000081b40000000000000000000000015ab217540000091f000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/checkMpathHP-UX.py import checks
from rcUtilities import justcall

class check(checks.check):
    chk_type = "mpath"
    svcdevs = {}

    def find_svc(self, dev):
        for svc in self.svcs:
            if svc not in self.svcdevs:
                try:
                    devs = svc.sub_devs()
                except Exception as e:
                    devs = []
                self.svcdevs[svc] = devs
            if dev in self.svcdevs[svc]:
                return svc.svcname
        return ''

    def do_check(self):
        cmd = ['scsimgr', 'lun_map']
        out, err, ret = justcall(cmd)
        if ret != 0:
            return self.undef
        lines = out.split('\n')
        if len(lines) < 1:
            return self.undef
        r = []
        dev = None
        wwid = None
        for line in lines:
            if "LUN PATH INFORMATION FOR LUN" in line:
                # new mpath
                # - store previous
                # - reset path counter
                if dev is not None and not dev.startswith('/dev/pt/pt') and wwid != '=' and "Virtual" not in proto:
                    r.append({'chk_instance': wwid,
                              'chk_value': str(n),
                              'chk_svcname': self.find_svc(dev),
                             })
                n = 0
                l = line.split()
                if len(l) < 2:
                    continue
                dev = l[-1]
            elif line.startswith("World Wide Identifier"):
                wwid = line.split()[-1].replace("0x","")
            elif line.startswith("SCSI transport protocol"):
                proto = line.split("=")[-1]
            elif line.startswith("State"):
                state = line.split("=")[-1].strip()
            elif line.startswith("Last Open or Close state"):
                last_known_state = line.split("=")[-1].strip()
                if state in ("ACTIVE", "STANDBY"):
                    n += 1
                elif state == "UNOPEN" and last_known_state in ("ACTIVE", "STANDBY"):
                    n += 1
        if dev is not None and not dev.startswith('/dev/pt/pt') and wwid != '=' and "Virtual" not in proto:
            r.append({'chk_instance': wwid,
                      'chk_value': str(n),
                      'chk_svcname': self.find_svc(dev),
            })
        return r
 0707010005c8a1000081b40000000000000000000000015ab21754000010f1000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/checkMpathLinux.py import checks
from rcUtilities import justcall, printplus
from rcGlobalEnv import rcEnv

class check(checks.check):
    chk_type = "mpath"
    svcdevs = {}

    def find_svc(self, dev):
        for svc in self.svcs:
            if svc not in self.svcdevs:
                try:
                    devs = svc.sub_devs()
                except Exception as e:
                    devs = []
                self.svcdevs[svc] = devs
            if dev in self.svcdevs[svc]:
                return svc.svcname
        return ''

    def do_check_old(self):
        """
	mpath1 (3600508b4000971ca0000f00010650000)
	[size=404 GB][features="1 queue_if_no_path"][hwhandler="0"]
	\_ round-robin 0 [active]
	 \_ 0:0:0:2 sda 8:0   [active]
	 \_ 1:0:0:2 sde 8:64  [active]
	 \_ 1:0:1:2 sdf 8:80  [active]
	 \_ 0:0:1:2 sdi 8:128 [active]
	\_ round-robin 0 [enabled]
	 \_ 0:0:2:2 sdc 8:32  [active]
	 \_ 0:0:3:2 sdd 8:48  [active]
	 \_ 1:0:3:2 sdh 8:112 [active]
	 \_ 1:0:2:2 sdb 8:16  [active]
        """
        cmd = [rcEnv.syspaths.multipath, '-l']
        (out, err, ret) = justcall(cmd)
        lines = out.split('\n')
        if len(lines) < 1:
            return self.undef
        r = []
        wwid = None
        dev = None
        for line in lines:
            if len(line) > 0 and not '\_ ' in line and not line.startswith('['):
                # new mpath
                # - store previous
                # - reset path counter
                if wwid is not None:
                    r.append({'chk_instance': wwid,
                              'chk_value': str(n),
                              'chk_svcname': self.find_svc(dev),
                             })
                n = 0
                l = line.split()
                if len(l) == 2:
                    wwid = l[1][1:-1]
                elif len(l) == 1:
                    wwid = l[0]
                else:
                    wwid = None
                if wwid is not None and len(wwid) in (17, 33) and wwid[0] in ('2', '3', '5'):
                    wwid = wwid[1:]
            if "[active]" in line and line.startswith(' '):
                n += 1
                dev = "/dev/"+line.split()[2]
        if wwid is not None:
            r.append({'chk_instance': wwid,
                      'chk_value': str(n),
                      'chk_svcname': self.find_svc(dev),
                     })
        return r

    def do_check(self):
        cmd = [rcEnv.syspaths.multipathd, '-kshow topo']
        (out, err, ret) = justcall(cmd)
        if 'list|show' in out:
            # multipathd does not support 'show topo'
            # try parsing 'multipath -l' output
            return self.do_check_old()
        if ret != 0:
            return self.undef
        lines = out.split('\n')
        if len(lines) < 1:
            return self.undef
        r = []
        wwid = None
        dev = None
        for line in lines:
            if ' dm-' in line:
                # new mpath
                # - store previous
                # - reset path counter
                if wwid is not None:
                    r.append({'chk_instance': wwid,
                              'chk_value': str(n),
                              'chk_svcname': self.find_svc(dev),
                             })
                n = 0
                if line.startswith(": "):
                    line = line.replace(": ", "")
                l = line.split()
                if l[0].endswith(":"):
                    # skip prefix: create, swithpg, reload, ...
                    l = l[1:]
                if len(l) < 2:
                    continue
                if l[1].startswith('('):
                    wwid = l[1][1:-1]
                else:
                    wwid = l[0]
                if wwid is not None and len(wwid) in (17, 33) and wwid[0] in ('2', '3', '5'):
                    wwid = wwid[1:]
            if "[active][ready]" in line or \
               "active ready" in line:
                n += 1
                dev = "/dev/"+line.split()[2]
        if wwid is not None:
            r.append({'chk_instance': wwid,
                      'chk_value': str(n),
                      'chk_svcname': self.find_svc(dev),
                     })
        return r

if __name__ == "__main__":
    paths = check()
    tab = paths.do_check()
    printplus(tab)
   0707010005c8a2000081b40000000000000000000000015ab2175400000363000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/checkMpathOSF1.py  import checks
from rcUtilities import justcall
from rcDiskInfoOSF1 import diskInfo

class check(checks.check):
    chk_type = "mpath"
    svcdevs = {}

    def find_svc(self, dev):
        devpath = '/dev/rdisk/'+dev
        for svc in self.svcs:
            if svc not in self.svcdevs:
                try:
                    devs = svc.sub_devs()
                except Exception as e:
                    devs = []
                self.svcdevs[svc] = devs
            if dev in self.svcdevs[svc]:
                return svc.svcname
        return ''

    def do_check(self):
        di = diskInfo()
        r = []
        for dev, data in di.h.items():
            r.append({'chk_instance': data['wwid'],
                      'chk_value': str(data['path_count']),
                      'chk_svcname': self.find_svc(dev),
                     })
        return r
 0707010005c8a3000081b40000000000000000000000015ab2175400000b0c000000660000000000000000000000000000003200000000root/usr/share/opensvc/lib/checkMpathPowerpath.py import checks
from rcUtilities import justcall, which
from rcGlobalEnv import rcEnv
di = __import__("rcDiskInfo"+rcEnv.sysname)
_di = di.diskInfo()

class check(checks.check):
    chk_type = "mpath"
    chk_name = "PowerPath"
    svcdevs = {}

    def find_svc(self, dev):
        for svc in self.svcs:
            if svc not in self.svcdevs:
                try:
                    devs = svc.sub_devs()
                except Exception as e:
                    devs = []
                self.svcdevs[svc] = devs
            if dev in self.svcdevs[svc]:
                return svc.svcname
        return ''

    def do_check(self):
        """
	Pseudo name=emcpowerh
	Symmetrix ID=000290101523
	Logical device ID=17C6
	state=alive; policy=SymmOpt; priority=0; queued-IOs=0
	==============================================================================
	---------------- Host ---------------   - Stor -   -- I/O Path -  -- Stats ---
	###  HW Path                I/O Paths    Interf.   Mode    State  Q-IOs Errors
	==============================================================================
	   0 qla2xxx                   sdi       FA  9dB   active  alive      0      0
	   1 qla2xxx                   sds       FA  8dB   active  alive      0      0
        """

        if not which('powermt'):
            return self.undef

        cmd = ['powermt', 'display', 'dev=all']
        (out, err, ret) = justcall(cmd)
        if ret != 0:
            return self.undef

        lines = out.split('\n')
        if len(lines) < 1:
            return self.undef

        r = []
        dev = None
        name = None
        paths = []
        n = 0
        for line in lines:
            if len(line) == 0:
                # new mpath
                # - store previous
                # - reset path counter
                if dev is not None:
                    if len(paths) > 0:
                        did = _di.disk_id(paths[0])
                        if did is not None:
                            name = did
                    r.append({'chk_instance': name,
                              'chk_value': str(n),
                              'chk_svcname': self.find_svc(dev),
                             })
                    paths = []
                    dev = None
                    n = 0
            if 'Pseudo name' in line:
                l = line.split('=')
                if len(l) != 2:
                    continue
                name = l[1]
                dev = "/dev/"+name
            else:
                l = line.split()
                if len(l) < 3:
                    continue
                if l[2].startswith("sd"):
                    paths.append("/dev/"+l[2])
                if "active" in line and \
                   "alive" in line:
                    n += 1
        return r
0707010005c8a4000081b40000000000000000000000015ab2175400000a30000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/checkMpathSunOS.py import checks
from rcUtilities import justcall

class check(checks.check):
    """
    # mpathadm list LU
        /dev/rdsk/c6t600507680280809AB0000000000000E7d0s2
                Total Path Count: 4
                Operational Path Count: 4
        /scsi_vhci/disk@g60050768018085d7e0000000000004e5
                Total Path Count: 1
                Operational Path Count: 1
        /dev/rdsk/c6t60050768018085D7E0000000000004E4d0s2
                Total Path Count: 1
                Operational Path Count: 1
        /dev/rdsk/c6t60050768018085D7E00000000000056Bd0s2
                Total Path Count: 4
                Operational Path Count: 4
    """
    chk_type = "mpath"
    svcdevs = {}

    def find_svc(self, dev):
        for svc in self.svcs:
            if svc not in self.svcdevs:
                try:
                    self.svcdevs[svc] = svc.sub_devs()
                except Exception as e:
                    self.svcdevs[svc] = []
            if dev in self.svcdevs[svc]:
                return svc.svcname
        return ''

    def do_check(self):
        cmd = ['mpathadm', 'list', 'LU']
        out, err, ret = justcall(cmd)
        if ret != 0:
            return self.undef
        lines = out.split('\n')
        if len(lines) < 4:
            return self.undef
        r = []
        dev = None
        wwid = ""
        for line in lines:
            if "/dev/" in line:
                # new mpath
                # - remember current dev
                # - remember current wwid
                # - reset path counter
                dev = line.strip()
                wwid = line[line.index('t')+1:line.rindex('d')]
                n = 0
            elif '/disk@g' in line:
                # unmapped dev
                # - remember current dev
                # - remember current wwid
                # - reset path counter
                dev = line.strip()
                wwid = '_'+line[line.index('@g')+2:]
                n = 0
            if "Total Path Count:" in line:
                continue
            if "Operational Path Count:" in line:
                # - store current dev if valid
                # - then:
                    # - reset path counter
                    # - reset dev
                n = int(line.split(':')[-1].strip())
                if dev is not None:
                    r.append({'chk_instance': wwid,
                              'chk_value': str(n),
                              'chk_svcname': self.find_svc(dev),
                             })
                    dev = None
                    n = 0
        return r

0707010005c8a5000081b40000000000000000000000015ab21754000005a0000000660000000000000000000000000000003000000000root/usr/share/opensvc/lib/checkMpathWindows.py   import checks
import wmi
import tempfile
from subprocess import *
import os

class check(checks.check):
    chk_type = "mpath"
    svcdevs = {}

    def find_svc(self, dev):
        for svc in self.svcs:
            if svc not in self.svcdevs:
                try:
                    devs = svc.sub_devs()
                except Exception as e:
                    devs = []
                self.svcdevs[svc] = devs
            if dev in self.svcdevs[svc]:
                return svc.svcname
        return ''

    def diskpart_rescan(self):
        f = tempfile.NamedTemporaryFile()
        tmpf = f.name
        f.close()
        with open(tmpf, 'w') as f:
            f.write("rescan\n")
        p = Popen(["diskpart", "/s", tmpf], stdout=PIPE, stderr=PIPE, stdin=None, shell=True)
        out, err = p.communicate()
        os.unlink(tmpf)

    def do_check(self):
        self.wmi = wmi.WMI(namespace="root/wmi")
        self.diskpart_rescan()
        r = []
        try:
            l = self.wmi.MPIO_DISK_INFO()
        except:
            l = []
        for disk in l:
            if disk.driveinfo is None:
                continue
            for drive in disk.driveinfo:
                name = drive.name
                n = drive.numberpaths
                r.append({'chk_instance': name,
                          'chk_value': str(n),
                          'chk_svcname': "",
                         })
        return r
0707010005c8a6000081b40000000000000000000000015ab217540000058b000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/checkNumaLinux.py  import checks
import os
import glob
import math

class check(checks.check):
    chk_type = "numa"

    def do_check(self):
        nodeinfo = {}
        memtotal = 0
        n_nodes = 0
        n_cpu = 0
        for npath in glob.glob("/sys/devices/system/node/node*"):
            node_n_cpu = len(glob.glob(npath+"/cpu*"))
            node = os.path.basename(npath)
            with open(npath+"/meminfo", 'r') as f:
                lines = f.read().strip('\n').split('\n')
                for line in lines:
                    if 'MemTotal' in line:
                        try:
                            node_mem = int(line.split()[-2])
                        except:
                            continue
                        memtotal += node_mem
                        n_nodes += 1
                        n_cpu += node_n_cpu
                        nodeinfo[node] = {"mem": node_mem, "cpu": node_n_cpu}
                        break
        r = []
        if n_nodes < 2:
            return r
        target_per_cpu = memtotal / n_cpu
        for node, info in nodeinfo.items():
            target = target_per_cpu * info['cpu']
            deviation = math.fabs(100. * (info['mem'] - target) // target)
            r.append({
                  'chk_instance': node+'.mem.leveling',
                  'chk_value': str(deviation),
                  'chk_svcname': '',
                 })
        return r

 0707010005c8a7000081b40000000000000000000000015ab21754000011f2000000660000000000000000000000000000003000000000root/usr/share/opensvc/lib/checkRaidMegaRaid.py   import checks
import os
from rcUtilities import justcall, which
from rcGlobalEnv import rcEnv

class check(checks.check):
    prefixes = [os.path.join(os.sep, "usr", "local", "admin"),
                os.path.join(os.sep, "opt", "MegaRAID", "MegaCli")]
    megacli = ["MegaCli64", "MegaCli", "megacli"]
    chk_type = "raid"
    chk_name = "MegaCli"

    def find_megacli(self):
        for prog in self.megacli:
            if which(prog):
                return prog
            for prefix in self.prefixes:
                megacli = os.path.join(prefix, prog)
                if os.path.exists(megacli):
                    return megacli
        return

    def do_check(self):
        r = self.do_check_ldpdinfo()
        r += self.do_check_bbustatus()
        return r

    def do_check_ldpdinfo(self):
        megacli = self.find_megacli()
        if megacli is None:
            return self.undef
        os.chdir(rcEnv.paths.pathtmp)
        logs = [os.path.join(rcEnv.paths.pathtmp, 'MegaSAS.log'),
                os.path.join(rcEnv.paths.pathtmp, 'MegaCli.log'),
                os.path.join(rcEnv.paths.pathtmp, 'MegaRaid.log')]
        for log in logs:
            if not os.path.exists(log):
                continue
            os.unlink(log)
        cmd = [megacli, '-LdPdInfo', '-aALL']
        out, err, ret = justcall(cmd)
        if ret != 0:
            return self.undef
        lines = out.split('\n')
        if len(lines) == 0:
            return self.undef
        r = []
        slot = ""
        errs = 0
        for line in lines:
            if line.startswith('Adapter'):
                l = line.split('#')
                if slot != "":
                    r.append({
                             'chk_instance': slot,
                              'chk_value': str(errs),
                              'chk_svcname': '',
                             })
                slot = 'slot'+l[-1]
                errs = 0
            if (line.startswith('State:') and 'Optimal' not in line) or \
               (line.startswith('Firmware state:') and 'Online' not in line):
                errs += 1
        if slot != "":
            r.append({
                 'chk_instance': slot,
                  'chk_value': str(errs),
                  'chk_svcname': '',
                 })
        return r

    def do_check_bbustatus(self):
        megacli = self.find_megacli()
        if megacli is None:
            return self.undef
        os.chdir(rcEnv.paths.pathtmp)
        logs = [os.path.join(rcEnv.paths.pathtmp, 'MegaSAS.log'),
                os.path.join(rcEnv.paths.pathtmp, 'MegaCli.log'),
                os.path.join(rcEnv.paths.pathtmp, 'MegaRaid.log')]
        for log in logs:
            if not os.path.exists(log):
                continue
            os.unlink(log)
        cmd = [megacli, '-AdpBbuCmd', '-GetBbuStatus', '-aALL']
        out, err, ret = justcall(cmd)
        if ret != 0:
            return self.undef
        lines = out.split('\n')
        if len(lines) == 0:
            return self.undef
        r = []
        slot = ""
        for line in lines:
            line = line.strip()
            if 'Adapter:' in line:
                l = line.split()
                slot = 'slot'+l[-1]
            if line.startswith('BatteryType:') and 'No Battery' in line:
                val = 1
                r.append({
                          'chk_instance': '%s battery NoBattery'%slot,
                          'chk_value': str(val),
                          'chk_svcname': '',
                         })
            if line.startswith('Relative State of Charge:'):
                val = line.strip('%').split()[-1]
                r.append({
                          'chk_instance': '%s battery charge'%slot,
                          'chk_value': str(val),
                          'chk_svcname': '',
                         })
            if line.startswith('Temperature:'):
                val = line.split()[-2]
                r.append({
                          'chk_instance': '%s battery temp'%slot,
                          'chk_value': str(val),
                          'chk_svcname': '',
                         })
            if line.startswith('isSOHGood:'):
                if 'Yes' in line:
                    val = 0
                else:
                    val = 1
                r.append({
                          'chk_instance': '%s battery isSOHGood'%slot,
                          'chk_value': str(val),
                          'chk_svcname': '',
                         })
        return r
  0707010005c8a8000081b40000000000000000000000015ab2175400000ec1000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/checkRaidSas2.py   import checks
import os
from rcUtilities import justcall, which
from rcGlobalEnv import rcEnv

class check(checks.check):
    prefixes = [os.path.join(os.sep, "usr", "local", "admin")]
    sas2ircu = "sas2ircu"
    chk_type = "raid"
    chk_name = "LSI SAS200"

    def find_sas2ircu(self):
        if which(self.sas2ircu):
            return self.sas2ircu
        for prefix in self.prefixes:
            sas2ircu = os.path.join(prefix, self.sas2ircu)
            if os.path.exists(sas2ircu):
                return sas2ircu
        return

    def do_check(self):
        r = self.do_check_ldpdinfo()
        return r

    def do_check_ldpdinfo(self):
        sas2ircu = self.find_sas2ircu()
        if sas2ircu is None:
            return self.undef
        os.chdir(rcEnv.paths.pathtmp)
        logs = [os.path.join(rcEnv.paths.pathtmp, 'sas2ircu.log')]
        for log in logs:
            if not os.path.exists(log):
                continue
            os.unlink(log)
        cmd = [sas2ircu, 'LIST']
        out, err, ret = justcall(cmd)
        if ret != 0:
            return self.undef
        idx = []
        lines = out.split('\n')
        for line in lines:
            if 'SAS20' in line:
                l = line.split()
                idx.append(l[0])

        r = []
        errs = 0
        for ix in idx:
            cmd = [sas2ircu, str(ix), 'DISPLAY']
            out, err, ret = justcall(cmd)
            lines = out.split('\n')
            ctrl = "ctrl:"+str(ix)
            slot=""
            chk_dsk = 0
            for line in lines:
                if line.startswith('IR volume'):
                    chk_dsk = 2
                if line.startswith('  Volume Name') and 'Virtual Disk' in line and (chk_dsk == 2):
                    l = line.split()
                    slot = 'LD'+str(l[-1])
                if line.startswith('  Status of volume') and (chk_dsk == 2):
                    if 'Okay (OKY)' not in line:
                        r.append({ 'chk_instance': ctrl+','+slot, 'chk_value': '1', 'chk_svcname': '', })
                        errs += 1
                    else :
                        r.append({ 'chk_instance': ctrl+','+slot, 'chk_value': '0', 'chk_svcname': '', })
                if line.startswith('Device is a Hard disk'):
                    chk_dsk = 1
                if line.startswith('  Enclosure #') and (chk_dsk == 1):
                    l = line.split()
                    enc = l[-1]
                if line.startswith('  Slot #') and (chk_dsk == 1):
                    l = line.split()
                    slot = 'PD'+str(enc)+':'+str(l[-1])
                if line.startswith('  State') and (chk_dsk == 1):
                    if 'Optimal (OPT)' not in line:
                        r.append({ 'chk_instance': ctrl+','+slot, 'chk_value': '1', 'chk_svcname': '', })
                        errs += 1
                    else :
                        r.append({ 'chk_instance': ctrl+','+slot, 'chk_value': '0', 'chk_svcname': '', })
                if line.startswith('Device is a Enclosure services device'):
                    chk_dsk = 3
                if line.startswith('  Enclosure #') and (chk_dsk == 3):
                    l = line.split()
                    slot = 'Enc'+str(l[-1])
                if line.startswith('  State') and (chk_dsk == 3):
                    if 'Standby (SBY)' not in line:
                        r.append({ 'chk_instance': ctrl+','+slot, 'chk_value': '1', 'chk_svcname': '', })
                        errs += 1
                    else :
                        r.append({ 'chk_instance': ctrl+','+slot, 'chk_value': '0', 'chk_svcname': '', })
            r.append({ 'chk_instance': 'all SAS20*', 'chk_value': str(errs), 'chk_svcname': '', })
        return r
   0707010005c8a9000081b40000000000000000000000015ab2175400000c17000000660000000000000000000000000000003200000000root/usr/share/opensvc/lib/checkRaidSmartArray.py import checks
from rcUtilities import justcall, which
import os

sep = ':'
path_list = os.environ['PATH'].split(sep) + ['/opt/HPQacucli/sbin']

os.environ['PATH'] = sep.join(path_list)
os.environ['INFOMGR_BYPASS_NONSA'] = '1'

class check(checks.check):
    chk_type = "raid"
    chk_name = "HP SmartArray"

    def parse_errors(self, out):
        r = []
        lines = out.split('\n')
        if len(lines) == 0:
            return r
        for line in lines:
            l = line.split(': ')
            if len(l) < 2 or not line.startswith('  '):
                continue
            if l[-1].strip() != "OK":
                inst = line.strip().lower()
                status = 1
            else:
                inst = l[0].strip().lower()
                status = 0
            r += [(inst, status)]
        return r

    def check_logicaldrive(self, slot):
        cmd = ['controller', 'slot='+slot, 'logicaldrive', 'all', 'show', 'status']
        out, err, ret = self.hpacucli(cmd)
        if ret != 0:
            return []
        return self.parse_errors(out)

    def check_physicaldrive(self, slot):
        cmd = ['controller', 'slot='+slot, 'physicaldrive', 'all', 'show', 'status']
        out, err, ret = self.hpacucli(cmd)
        if ret != 0:
            return []
        return self.parse_errors(out)

    def check_array(self, slot):
        cmd = ['controller', 'slot='+slot, 'array', 'all', 'show', 'status']
        out, err, ret = self.hpacucli(cmd)
        if ret != 0:
            return []
        return self.parse_errors(out)

    def check_controller(self, slot):
        cmd = ['controller', 'slot='+slot, 'show', 'status']
        out, err, ret = self.hpacucli(cmd)
        if ret != 0:
            return []
        return self.parse_errors(out)

    def hpacucli(self, cmd):
        cmd = ['hpacucli'] + cmd
        try:
            out, err, ret = justcall(cmd)
        except OSError:
            cmd = [os.environ['SHELL']] + cmd
            out, err, ret = justcall(cmd)
        return out, err, ret

    def do_check(self):
        if not which('hpacucli'):
            return self.undef
        cmd = ['controller', 'all', 'show', 'status']
        out, err, ret = self.hpacucli(cmd)
        if ret != 0:
            return self.undef
        lines = out.split('\n')
        if len(lines) == 0:
            return self.undef
        r = []
        for line in lines:
            if ' Slot ' in line:
                l = line.split()
                idx = l.index('Slot')
                uslot = l[idx+1]
                slot = 'slot ' + uslot
                _r = []
                _r += self.check_controller(uslot)
                _r += self.check_array(uslot)
                _r += self.check_logicaldrive(uslot)
                _r += self.check_physicaldrive(uslot)
                for inst, value in _r:
                    r.append({
                          'chk_instance': ".".join((slot, inst)),
                          'chk_value': str(value),
                          'chk_svcname': '',
                         })
        return r
 0707010005c8aa000081b40000000000000000000000015ab2175400000208000000660000000000000000000000000000003900000000root/usr/share/opensvc/lib/checkRaidSmartArrayWindows.py  import checkRaidSmartArray
import os

sep = ';'
path_list = os.environ['PATH'].split(sep)

if 'PROGRAMFILES' in os.environ:
    path_list.append(os.path.join(os.environ.get('PROGRAMFILES'),
                                  'compaq', 'hpacucli', 'bin'))
if 'PROGRAMFILES(X86)' in os.environ:
    path_list.append(os.path.join(os.environ.get('PROGRAMFILES(X86)'),
                                  'compaq', 'hpacucli', 'bin'))

os.environ['PATH'] = sep.join(path_list)

class check(checkRaidSmartArray.check):
    pass

0707010005c8ab000081b40000000000000000000000015ab21754000004e0000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/checkSync.py   import checks

SYNC_DRIVERS = [
    "sync.btrfs",
    "sync.dds",
    "sync.docker",
    "sync.rsync",
    "sync.zfs",
]

class check(checks.check):
    chk_type = "sync"

    def find_svc(self, vgname):
        for svc in self.svcs:
            for resource in svc.get_resources('disk'):
                if not hasattr(resource, "name"):
                    continue
                if resource.name == vgname:
                    return svc.svcname
        return ''

    def do_check(self):
        data = []
        for svc in self.svcs:
            for resource in svc.get_resources(SYNC_DRIVERS):
                data += self.check_resource(svc, resource)
        return data

    def check_resource(self, svc, resource):
        data = []
        stats = resource.load_stats()
        if "bytes" in stats:
            data.append({
                'chk_instance': resource.rid + ".bytes",
                'chk_value': str(stats["bytes"]),
                'chk_svcname': svc.svcname,
             })
        if "speed" in stats:
            data.append({
                'chk_instance': resource.rid + ".speed",
                'chk_value': str(stats["speed"]),
                'chk_svcname': svc.svcname,
             })
        return data
0707010005c8ac000081b40000000000000000000000015ab21754000004e7000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/checkVgUsageAIX.py import checks
from rcUtilities import justcall

class check(checks.check):
    chk_type = "vg_u"

    def find_svc(self, vgname):
        for svc in self.svcs:
            for resource in svc.get_resources('disk.vg'):
                if resource.name == vgname:
                    return svc.svcname
        return ''

    def do_check(self):
        r = []
        cmd = ['lsvg']
        out, err, ret = justcall(cmd)
        if ret != 0:
            return self.undef
        vgs = out.split('\n')
        for vg in vgs:
            r += self._do_check(vg)
        return r

    def _do_check(self, vg):
        cmd = ['lsvg', '-p', vg]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return self.undef
        lines = out.split('\n')
        if len(lines) < 3:
            return self.undef
        r = []
        for line in lines[2:]:
            l = line.split()
            if len(l) != 5:
                continue
            size = int(l[2])
            free = int(l[3])
            val = int(100*(size-free)/size)
            r.append({'chk_instance': vg,
                      'chk_value': str(val),
                      'chk_svcname': self.find_svc(l[0]),
                     }
                    )
        return r
 0707010005c8ad000081b40000000000000000000000015ab21754000006ec000000660000000000000000000000000000003000000000root/usr/share/opensvc/lib/checkVgUsageHP-UX.py   import checks
from rcUtilities import call

class check(checks.check):
    chk_type = "vg_u"

    def find_svc(self, vgname):
        for svc in self.svcs:
            for resource in svc.get_resources('disk.vg'):
                if resource.name == vgname:
                    return svc.svcname
        return ''

    def do_check(self):
        """
        # vgdisplay -F
vg_name=/dev/vg00:vg_write_access=read,write:vg_status=available:max_lv=255:cur_lv=9:open_lv=9:max_pv=16:cur_pv=1:act_pv=1:max_pe_per_pv=4384:vgda=2:pe_size=32:total_pe=4347:alloc_pe=2712:free_pe=1635:total_pvg=0:total_spare_pvs=0:total_spare_pvs_in_use=0:vg_version=1.0:vg_max_size=2192g:vg_max_extents=70144
        """
        cmd = ['vgdisplay', '-F']
        (ret, out, err) = call(cmd, errlog=False)
        if ret != 0:
            return self.undef
        lines = out.split('\n')
        if len(lines) < 1:
            return self.undef
        r = []
        for line in lines:
            l = line.split(':')
            if len(l) < 10:
                continue
            instance = None
            free = None
            size = None
            for w in l:
                if 'vg_name' in w:
                    instance = w.split('=')[1].replace('/dev/','')
                elif 'total_pe' in w:
                    size = int(w.split('=')[1])
                elif 'free_pe' in w:
                    free = int(w.split('=')[1])
            if instance is None or free is None or size is None:
                continue
            val = int(100*(size-free)/size)
            r.append({'chk_instance': instance,
                      'chk_value': str(val),
                      'chk_svcname': self.find_svc(instance),
                     }
                    )
        return r
0707010005c8ae000081b40000000000000000000000015ab21754000004ba000000660000000000000000000000000000003000000000root/usr/share/opensvc/lib/checkVgUsageLinux.py   import checks
from rcUtilities import justcall
from rcGlobalEnv import rcEnv

class check(checks.check):
    chk_type = "vg_u"

    def find_svc(self, vgname):
        for svc in self.svcs:
            for resource in svc.get_resources('disk'):
                if not hasattr(resource, "name"):
                    continue
                if resource.name == vgname:
                    return svc.svcname
        return ''

    def do_check(self):
        cmd = [rcEnv.syspaths.vgs, '--units', 'b', '--noheadings',
               '-o', 'vg_name,vg_size,vg_free']
        (out,err,ret) = justcall(cmd)
        if ret != 0:
            return self.undef
        lines = out.split('\n')
        if len(lines) < 1:
            return self.undef
        r = []
        for line in lines:
            l = line.split()
            if len(l) != 3:
                continue
            size = int(l[1].replace('B',''))
            free = int(l[2].replace('B',''))
            val = int(100*(size-free)/size)
            r.append({'chk_instance': l[0],
                      'chk_value': str(val),
                      'chk_svcname': self.find_svc(l[0]),
                     }
                    )
        return r
  0707010005c8af000081b40000000000000000000000015ab2175400000c0d000000660000000000000000000000000000003100000000root/usr/share/opensvc/lib/checkZfsUsageSunOS.py  import re
import checks
from rcUtilities import justcall, which
from rcGlobalEnv import rcEnv

class check(checks.check):
    def __init__(self, svcs=[]):
        checks.check.__init__(self, svcs)
        self.zpcache = {}

    chk_type = "fs_u"

    def convert(self, s):
        s = s.replace(',', '.').upper()
        if s == "0":
            return 0
        if len(s) < 2:
            raise
        if s.endswith('T'):
            s = float(s[:-1])*1024*1024*1024
        elif s.endswith('G'):
            s = float(s[:-1])*1024*1024
        elif s.endswith('M'):
            s = float(s[:-1])*1024
        elif s.endswith('K'):
            s = float(s[:-1])
        else:
            raise
        return s

    def get_zonepath(self, name):
        if name in self.zpcache:
            return self.zpcache[name]
        if which("zonecfg") is None:
            return
        cmd = ['zonecfg', '-z', name, 'info', 'zonepath']
        (out,err,ret) = justcall(cmd)
        if ret != 0:
            return None
        self.zpcache[name] = out.split()[-1].strip()
        return self.zpcache[name]

    def find_svc(self, name, mnt):
        for svc in self.svcs:
            for resource in svc.get_resources('container.zone'):
                zpath = self.get_zonepath(resource.name)
                if zpath is not None and zpath == mnt:
                    return svc.svcname
            for resource in svc.get_resources('fs'):
                if hasattr(resource, "device") and resource.device == name:
                    return svc.svcname
        return ''

    def do_check(self):
        cmd = [rcEnv.syspaths.zfs, 'list', '-o', 'name,used,avail,mountpoint', '-H']
        (out,err,ret) = justcall(cmd)
        if ret != 0:
            return self.undef
        lines = out.split('\n')
        if len(lines) == 0:
            return self.undef
        r = []
        for line in lines:
            l = line.split()
            if len(l) != 4:
                continue
            if "@" in l[0]:
                # do not report clone usage
                continue
            if re.findall("/[0-9a-f]{64}", l[0]):
                # docker id
                continue
            if "osvc_sync_" in l[0]:
                # do not report osvc sync snapshots fs usage
                continue
            used = self.convert(l[1])
            avail = self.convert(l[2])
            total = used + avail
            pct = used / total * 100
            svcname = self.find_svc(l[0], l[3])
            r.append({
                      'chk_instance': l[0],
                      'chk_value': str(pct),
                      'chk_svcname': svcname,
                     })
            r.append({
                      'chk_instance': l[0]+".free",
                      'chk_value': str(avail),
                      'chk_svcname': svcname,
                     })
            r.append({
                      'chk_instance': l[0]+".size",
                      'chk_value': str(total),
                      'chk_svcname': svcname,
                     })
        return r
   0707010005c8b0000081b40000000000000000000000015ab2175400000895000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/checkZpoolSunOS.py import checks
from rcUtilities import justcall
import os
import re
from rcGlobalEnv import rcEnv

class check(checks.check):
    """
    # zpool status
      pool: rpool
     state: DEGRADED
    status: One or more devices has been taken offline by the administrator.
            Sufficient replicas exist for the pool to continue functioning in a
            degraded state.
    action: Online the device using 'zpool online' or replace the device with
            'zpool replace'.
     scrub: scrub completed after 0h40m with 0 errors on Sun Jun 24 05:41:27 2012
    config:

            NAME          STATE     READ WRITE CKSUM
            rpool         DEGRADED     0     0     0
              mirror      DEGRADED     0     0     0
                c0t0d0s0  ONLINE       0     0     0
                c0t1d0s0  OFFLINE      0     0     0
    """
    chk_type = "zpool"

    def find_svc(self, pool):
        for svc in self.svcs:
            for res in svc.get_resources("disk.zpool"):
                if res.name == pool:
                    return svc.svcname
        return ''

    def do_check(self):
        cmd = ['zpool', 'list', '-H', '-o', 'name,health']
        out, err, ret = justcall(cmd)
        if ret != 0:
            return self.undef
        lines = out.split('\n')
        if len(lines) < 2:
            return self.undef
        r = []
        for line in lines:
            if len(line) < 2:
                continue
            l = line.split()
            pool = l[0]
            stat = l[1]
            if stat == 'ONLINE':
                stat_val = 0
            elif stat == 'DEGRADED':
                stat_val = 1
            elif stat == 'FAULTED':
                stat_val = 2
            elif stat == 'OFFLINE':
                stat_val = 3
            elif stat == 'REMOVED':
                stat_val = 4
            elif stat == 'UNAVAIL':
                stat_val = 5
            else:
                stat_val = 6
            if pool is not None:
                r.append({'chk_instance': pool,
                          'chk_value': str(stat_val),
                          'chk_svcname': self.find_svc(pool),
                        })
        return r
   0707010005c8b1000081b40000000000000000000000015ab217540000147f000000660000000000000000000000000000002500000000root/usr/share/opensvc/lib/checks.py  from __future__ import print_function
from rcGlobalEnv import rcEnv
from rcUtilities import mimport
import os

class check(object):
    undef = [{
              'check_svcname': '',
              'chk_instance': 'undef',
              'chk_value': '-1'
             }]
    def __init__(self, svcs=[]):
        self.svcs = svcs
        if self.svcs is None:
            self.svcs = []

    def do_check(self): # pragma: no cover
        """
        To be implemented by child classes.
        """
        return []

class checks(check):
    check_list = []

    def __init__(self, svcs=[]):
        self.svcs = svcs
        self.register('Fs', 'Usage')
        self.register('Fs', 'Inode')
        self.register('Vg', 'Usage')
        self.register('Eth')
        self.register('Lag')
        self.register('Mpath')
        self.register('Mpath', 'Powerpath')
        self.register('Zfs', 'Usage')
        self.register('Raid', 'Smart', 'Array')
        self.register('Raid', 'Mega', 'Raid')
        self.register('Raid', 'Sas2')
        self.register('Fm', 'Fmadm')
        self.register('Fm', 'Open', 'Manage')
        self.register('Mce')
        self.register('Zpool')
        self.register('Btrfs', 'Dev', 'Stats')
        self.register('Advfs', 'Usage')
        self.register('Numa')
        self.register('Sync')
        self.register_local_checkers()

    def __iadd__(self, c):
        if isinstance(c, check):
            self.check_list.append(c)
        elif isinstance(c, checks):
            self.check_list += c.check_list
        return self

    def register_local_checkers(self):
        import os
        import glob
        check_d = os.path.join(rcEnv.paths.pathvar, 'check')
        if not os.path.exists(check_d):
            return
        import sys
        sys.path.append(check_d)
        for f in glob.glob(os.path.join(check_d, 'check*.py')):
            if rcEnv.sysname not in f:
                continue
            cname = os.path.basename(f).replace('.py', '')
            try:
                m = __import__(cname)
                self += m.check(svcs=self.svcs)
            except Exception as e:
                print('Could not import check:', cname, file=sys.stderr)
                print(e, file=sys.stderr)

    def register(self, *args):
        try:
            m = mimport("check", *args)
        except ImportError:
            return
        self += m.check(svcs=self.svcs)

    def do_checks(self):
        import datetime

        now = str(datetime.datetime.now())
        data = {}
        vars = [\
            "chk_nodename",
            "chk_svcname",
            "chk_type",
            "chk_instance",
            "chk_value",
            "chk_updated"]
        vals = []

        for chk in self.check_list:
            idx = chk.chk_type
            if hasattr(chk, "chk_name"):
                driver = chk.chk_name.lower()
            else:
                driver = "generic"

            _data = chk.do_check()

            if not isinstance(_data, (list, tuple)) or len(_data) == 0:
                continue

            instances = []
            for instance in _data:
                if not isinstance(instance, dict):
                    continue
                if 'chk_instance' not in instance:
                    continue
                if instance['chk_instance'] == 'undef':
                    continue
                if 'chk_value' not in instance:
                    continue
                _instance = {
                    "instance": instance.get("chk_instance", ""),
                    "value": instance.get("chk_value", ""),
                    "svcname": instance.get("chk_svcname", ""),
                    "driver": driver,
                }

                vals.append([\
                    rcEnv.nodename,
                    _instance["svcname"],
                    chk.chk_type,
                    _instance['instance'],
                    str(_instance['value']).replace("%",""),
                    now]
                )

                instances.append(_instance)

            if len(instances) > 0:    
                if idx not in data:
                    data[idx] = instances
                else:
                    data[idx] += instances

        self.node.collector.call('push_checks', vars, vals)
        if self.node.options.format is None:
            self.print_checks(data)
            return
        return data

    def print_checks(self, data):
        from forest import Forest
        from rcColor import color
        tree = Forest()
        head_node = tree.add_node()
        head_node.add_column(rcEnv.nodename, color.BOLD)
        for chk_type, instances in data.items():
            node = head_node.add_node()
            node.add_column(chk_type, color.BROWN)
            for instance in instances:
                _node = node.add_node()
                _node.add_column(str(instance["instance"]), color.LIGHTBLUE)
                _node.add_column(instance["svcname"])
                _node.add_column(str(instance["value"]))
                if instance["driver"] == "generic":
                    _node.add_column()
                else:
                    _node.add_column(instance["driver"])
        print(tree)

 0707010005c8b2000081b40000000000000000000000015ab2175400002bf9000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/collector.py   from __future__ import print_function
from stat import *
import os
import sys
import re
import datetime
import rcExceptions as ex
from rcGlobalEnv import rcEnv
from rcUtilities import justcall, banner
from converters import convert_duration
from subprocess import *

class Collector(object):
    def __init__(self, options=None, node=None, svcname=None):
        self.options = options
        self.node = node
        self.collector = node.collector
        self.svcname = svcname
        self.options = options

    def rotate_root_pw(self, pw):
        opts = {}
        opts['pw'] = pw
        d = self.collector.call('collector_update_root_pw', opts)
        if d is None:
            raise ex.excError("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.excError(d['msg'])

    def collector_ack_unavailability(self):
        if self.svcname is None:
            return

        opts = {}
        opts['svcname'] = self.svcname
        if self.options.begin is not None:
            opts['begin'] = self.options.begin
        if self.options.end is not None:
            opts['end'] = self.options.end
        if self.options.author is not None:
            opts['author'] = self.options.author
        if self.options.comment is not None:
            opts['comment'] = self.options.comment
        if self.options.duration is not None:
            opts['duration'] = convert_duration(self.options.duration, _to="m")
        if self.options.account:
            opts['account'] = "1"
        else:
            opts['account'] = "0"

        d = self.collector.call('collector_ack_unavailability', opts)
        if d is None:
            raise ex.excError("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.excError(d['msg'])

    def collector_list_unavailability_ack(self):
        if self.svcname is None:
            return

        opts = {}
        opts['svcname'] = self.svcname
        if self.options.begin is not None:
            opts['begin'] = self.options.begin
        if self.options.end is not None:
            opts['end'] = self.options.end
        if self.options.author is not None:
            opts['author'] = self.options.author
        if self.options.comment is not None:
            opts['comment'] = self.options.comment
        if self.options.duration is not None:
            opts['duration'] = convert_duration(self.options.duration, _to="m")
        if self.options.account:
            opts['account'] = "1"
        else:
            opts['account'] = "0"

        d = self.collector.call('collector_list_unavailability_ack', opts)
        if d is None:
            raise ex.excError("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.excError(d['msg'])

        return d['data']

    def collector_list_actions(self):
        opts = {}
        if self.svcname is not None:
            opts['svcname'] = self.svcname
        if self.options.begin is not None:
            opts['begin'] = self.options.begin
        if self.options.end is not None:
            opts['end'] = self.options.end
        if self.options.duration is not None:
            opts['duration'] = convert_duration(self.options.duration, _to="m")

        d = self.collector.call('collector_list_actions', opts)
        if d is None:
            raise ex.excError("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.excError(d['msg'])

        return d['data']

    def collector_ack_action(self):
        opts = {}
        if self.svcname is not None:
            opts['svcname'] = self.svcname
        if self.options.author is not None:
            opts['author'] = self.options.author
        if self.options.comment is not None:
            opts['comment'] = self.options.comment
        if self.options.id == 0:
            raise ex.excError("--id is not set")
        else:
            opts['id'] = self.options.id

        d = self.collector.call('collector_ack_action', opts)
        if d is None:
            raise ex.excError("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.excError(d['msg'])

    def collector_networks(self):
        opts = {}
        if self.svcname is not None:
            opts['svcname'] = self.svcname
        d = self.collector.call('collector_networks', opts)
        if d is None:
            raise ex.excError("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.excError(d['msg'])

        return d['data']

    def collector_asset(self):
        opts = {}
        if self.svcname is not None:
            opts['svcname'] = self.svcname
        d = self.collector.call('collector_asset', opts)
        if d is None:
            raise ex.excError("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.excError(d['msg'])

        return d['data']

    def collector_checks(self):
        opts = {}
        if self.svcname is not None:
            opts['svcname'] = self.svcname
        d = self.collector.call('collector_checks', opts)
        if d is None:
            raise ex.excError("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.excError(d['msg'])

        return d['data']

    def collector_disks(self):
        opts = {}
        if self.svcname is not None:
            opts['svcname'] = self.svcname
        d = self.collector.call('collector_disks', opts)
        if d is None:
            raise ex.excError("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.excError(d['msg'])

        return d['data']

    def collector_alerts(self):
        opts = {}
        if self.svcname is not None:
            opts['svcname'] = self.svcname
        d = self.collector.call('collector_alerts', opts)
        if d is None:
            raise ex.excError("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.excError(d['msg'])

        return d['data']

    def collector_events(self):
        opts = {}
        if self.svcname is not None:
            opts['svcname'] = self.svcname
        if self.options.begin is not None:
            opts['begin'] = self.options.begin
        if self.options.end is not None:
            opts['end'] = self.options.end
        d = self.collector.call('collector_events', opts)
        if d is None:
            raise ex.excError("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.excError(d['msg'])

        return d['data']

    def collector_show_actions(self):
        opts = {}
        if self.svcname is not None:
            opts['svcname'] = self.svcname
        if self.options.id != 0:
            opts['id'] = self.options.id
        if self.options.begin is not None:
            opts['begin'] = self.options.begin
        if self.options.end is not None:
            opts['end'] = self.options.end
        d = self.collector.call('collector_show_actions', opts)
        if d is None:
            raise ex.excError("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.excError(d['msg'])

        return d['data']

    def collector_untag(self):
        opts = {}
        opts['tag_name'] = self.options.tag
        if self.svcname:
            opts['svcname'] = self.svcname
        d = self.collector.call('collector_untag', opts)
        if d is None:
            raise ex.excError("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.excError(d['msg'])

    def collector_tag(self):
        opts = {}
        opts['tag_name'] = self.options.tag
        if self.svcname:
            opts['svcname'] = self.svcname
        d = self.collector.call('collector_tag', opts)
        if d is None:
            raise ex.excError("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.excError(d['msg'])

    def collector_create_tag(self):
        opts = {}
        opts['tag_name'] = self.options.tag
        if opts['tag_name'] is None:
            print("missing parameter: --tag", file=sys.stderr)
            return 1
        if self.svcname:
            opts['svcname'] = self.svcname
        d = self.collector.call('collector_create_tag', opts)
        if d is None:
            raise ex.excError("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.excError(d['msg'])

    def collector_list_tags(self):
        d = self._collector_list_tags()
        for tag in d:
            print(tag)

    def _collector_list_tags(self):
        opts = {'pattern': self.options.like}
        if self.svcname:
            opts['svcname'] = self.svcname
        d = self.collector.call('collector_list_tags', opts)
        if d is None:
            raise ex.excError("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.excError(d['msg'])
        return d['data']

    def collector_show_tags(self):
        opts = {}
        if self.svcname:
            opts['svcname'] = self.svcname
        d = self.collector.call('collector_show_tags', opts)
        if d is None:
            raise ex.excError("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.excError(d['msg'])
        return d['data']

    def collector_list_nodes(self):
        opts = {'fset': self.options.filterset}
        d = self.collector.call('collector_list_nodes', opts)
        if d is None:
            raise ex.excError("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.excError(d['msg'])
        return d['data']

    def collector_list_services(self):
        opts = {'fset': self.options.filterset}
        d = self.collector.call('collector_list_services', opts)
        if d is None:
            raise ex.excError("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.excError(d['msg'])
        return d['data']

    def collector_list_filtersets(self):
        opts = {'fset': self.options.filterset}
        d = self.collector.call('collector_list_filtersets', opts)
        if d is None:
            raise ex.excError("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.excError(d['msg'])
        return d['data']

    def collector_search(self):
        path = "/search?"
        if self.options.like.count(":") == 1:
            t, s = self.options.like.split(":")
            t = t.strip()
            s = s.strip()
            path += "substring=%s&in=%s" % (s, t)
        else:
            s = self.options.like
            path += "substring=%s" % s
        d = self.node.collector_rest_get(path)
        if "data" not in d:
            raise ex.excError("unexpected collector response: %s" % str(d))
        data = []
        for t, _d in d["data"].items():
            if _d["total"] == 0:
                continue
            print("%s (%d/%d)"  % (t, len(_d["data"]), _d["total"]))
            for e in d["data"][t]["data"]:
                e_name = _d["fmt"]["name"] % e
                e_id = _d["fmt"]["id"] % e
                print(" %s: %s" % (e_id, e_name))

    def collector_log(self):
        path = "/logs"
        data = {
          "log_fmt": self.options.message,
        }
        d = self.node.collector_rest_post(path, data, svcname=self.svcname)
        if "error" in d:
            raise ex.excError(d["error"])
        print("logged")

   0707010005c8b3000081b40000000000000000000000015ab21754000050f1000000660000000000000000000000000000002300000000root/usr/share/opensvc/lib/comm.py    """
The daemon encrypted communications primitives.
"""

import base64
import json
import os
import socket
import sys
import threading
import zlib
import time
import select

import pyaes
import rcExceptions as ex
from rcGlobalEnv import rcEnv, Storage
from rcUtilities import lazy, bdecode

if sys.version_info[0] >= 3:
    to_bytes = lambda x: bytes(x, "utf-8")
else:
    to_bytes = lambda x: bytes(x)

# Number of received misencrypted data messages by senders
BLACKLIST = {}
BLACKLIST_LOCK = threading.RLock()

# The maximum number of misencrypted data messages received before refusing
# new messages
BLACKLIST_THRESHOLD = 5

try:
    from Crypto.Cipher import AES
    def _encrypt(message, key, _iv):
        """
        Low level encrypter.
        """
        message = pyaes.util.append_PKCS7_padding(zlib.compress(message.encode()))
        obj = AES.new(key, AES.MODE_CBC, _iv)
        ciphertext = obj.encrypt(message)
        return ciphertext
    def _decrypt(ciphertext, key, _iv):
        """
        Low level decrypter.
        """
        obj = AES.new(key, AES.MODE_CBC, _iv)
        message = obj.decrypt(ciphertext)
        return zlib.decompress(pyaes.util.strip_PKCS7_padding(message))
except ImportError:
    def _encrypt(message, key, _iv):
        """
        Low level encrypter.
        """
        obj = pyaes.Encrypter(pyaes.AESModeOfOperationCBC(key, iv=_iv))
        ciphertext = obj.feed(zlib.compress(message.encode()))
        ciphertext += obj.feed()
        return ciphertext
    def _decrypt(ciphertext, key, _iv):
        """
        Low level decrypter.
        """
        obj = pyaes.Decrypter(pyaes.AESModeOfOperationCBC(key, iv=_iv))
        message = obj.feed(ciphertext)
        message += obj.feed()
        return zlib.decompress(message)

class Crypt(object):
    """
    A class implement AES encrypt, decrypt and message padding.
    Used by child classes to authenticate senders on data receive.
    """
    @staticmethod
    def _encrypt(message, key, _iv):
        """
        A wrapper over the low level encrypter.
        """
        return _encrypt(message, key, _iv)

    @staticmethod
    def _decrypt(ciphertext, key, _iv):
        """
        A wrapper over the low level decrypter.
        """
        return _decrypt(ciphertext, key, _iv)

    @staticmethod
    def gen_iv(urandom=[], locker=threading.RLock()):
        """
        This is 4x faster than calling os.urandom(16) and prevents
        the "too many files open" issue with concurrent access to os.urandom()
        """
        try:
            return urandom.pop()
        except IndexError:
            try:
                locker.acquire()
                ur = os.urandom(16 * 1024)
                urandom += [ur[i:i + 16] for i in range(16, 1024 * 16, 16)]
                return ur[0:16]
            finally:
                locker.release()

    @lazy
    def sorted_cluster_nodes(self):
        return sorted(self.cluster_nodes)

    @lazy
    def arbitrators(self):
        if hasattr(self, "get_node"):
            config = self.get_node().config
        else:
            config = self.config
        arbitrators = []
        for section in config.sections():
            data = {}
            if not section.startswith("arbitrator#"):
                continue
            try:
                data["name"] = config.get(section, "name")
            except Exception:
                continue
            try:
                data["secret"] = config.get(section, "secret")
            except Exception:
                continue
            arbitrators.append(data)
        return arbitrators

    @lazy
    def cluster_nodes(self):
        """
        Return the cluster nodes, read from cluster.nodes in the node
        configuration. If not set, return a list with the local node as the
        only element.
        """
        nodes = None
        if hasattr(self, "get_node"):
            config = self.get_node().config
        else:
            config = self.config
        try:
            nodes = config.get("cluster", "nodes").split()
        except Exception as exc:
            pass

        if nodes is not None:
            if rcEnv.nodename in nodes:
                return nodes
            else:
                nodes.append(rcEnv.nodename)
        else:
            nodes = [rcEnv.nodename]

        if hasattr(self, "get_node"):
            node = self.get_node()
        elif hasattr(self, "write_config"):
            node = self
        else:
            node = None
        if node is not None:
            if not node.config.has_section("cluster"):
                node.config.add_section("cluster")
            node.config.set("cluster", "nodes", " ".join(nodes))
            node.write_config()
        return nodes

    @lazy
    def cluster_drpnodes(self):
        """
        Return the cluster drp nodes, read from cluster.drpnodes in the node
        configuration. If not set, return an empty list.
        """
        nodes = []
        if hasattr(self, "get_node"):
            config = self.get_node().config
        else:
            config = self.config
        try:
            nodes = config.get("cluster", "drpnodes").split()
        except Exception as exc:
            pass
        return nodes

    @lazy
    def cluster_name(self):
        """
        Return the cluster name, read from cluster.name in the node
        configuration. If not set, return "default".
        """
        if hasattr(self, "get_node"):
            config = self.get_node().config
        else:
            config = self.config
        try:
            return config.get("cluster", "name")
        except Exception as exc:
            pass
        if hasattr(self, "node"):
            node = self.node
        elif hasattr(self, "write_config"):
            node = self
        else:
            node = None
        name = "default"
        if node is not None:
            if not node.config.has_section("cluster"):
                node.config.add_section("cluster")
            node.config.set("cluster", "name", name)
            node.write_config()
        return name

    @lazy
    def cluster_key(self):
        """
        Return the key read from cluster.secret in the node configuration.
        If not already set generate and store a random one.
        """
        if hasattr(self, "get_node"):
            config = self.get_node().config
        else:
            config = self.config
        try:
            key = config.get("cluster", "secret")
            return self.prepare_key(key)
        except Exception as exc:
            pass
        if hasattr(self, "node"):
            node = self.node
        elif hasattr(self, "write_config"):
            node = self
        else:
            return
        import uuid
        key = uuid.uuid1().hex
        if not config.has_section("cluster"):
            config.add_section("cluster")
        node.config.set("cluster", "secret", key)
        node.write_config()
        return self.prepare_key(key)

    @lazy
    def cluster_id(self):
        """
        Return the cluster id read from cluster.id in the node configuration.
        If not already set generate and store a random one.
        """
        if hasattr(self, "get_node"):
            # svc
            node = self.get_node()
        elif hasattr(self, "write_config"):
            # node
            node = self
        else:
            from osvcd_shared import NODE
            node = NODE
        try:
            return node.config.get("cluster", "id")
        except Exception as exc:
            pass
        import uuid
        cluster_id = str(uuid.uuid1())
        if not node.config.has_section("cluster"):
            node.config.add_section("cluster")
        node.config.set("cluster", "id", cluster_id)
        node.write_config()
        return cluster_id

    @staticmethod
    def prepare_key(key):
        """
        Return the key in a format expected by the encrypter and decrypter.
        """
        key = key.encode("utf-8")
        if len(key) > 32:
            key = key[:32]
        return key

    def msg_encode(self, data):
        return (json.dumps(data)+'\0').encode()

    def msg_decode(self, message):
        message = bdecode(message).rstrip("\0\x00")
        if len(message) == 0:
            return
        return json.loads(message)

    def decrypt(self, message, cluster_name=None, secret=None, sender_id=None):
        """
        Validate the message meta, decrypt and return the data.
        """
        if cluster_name is None:
            cluster_name = self.cluster_name
        if secret is None:
            cluster_key = self.cluster_key
        else:
            cluster_key = secret
        message = bdecode(message).rstrip("\0\x00")
        try:
            message = json.loads(message)
        except ValueError:
            message_len = len(message)
            if message_len > 40:
                self.log.error("misformatted encrypted message from %s: %s",
                               sender_id, message[:30]+"..."+message[-10:])
            elif message_len > 0:
                self.log.error("misformatted encrypted message from %s",
                               sender_id)
            return None, None
        if cluster_name != "join" and \
           message.get("clustername") not in (cluster_name, "join"):
            self.log.warning("discard message from cluster %s, sender %s",
                             message.get("clustername"), sender_id)
            return None, None
        if cluster_key is None:
            return None, None
        nodename = message.get("nodename")
        if nodename is None:
            return None, None
        iv = message.get("iv")
        if iv is None:
            return None, None
        if self.blacklisted(sender_id):
            return None, None
        iv = base64.urlsafe_b64decode(to_bytes(iv))
        data = base64.urlsafe_b64decode(to_bytes(message["data"]))
        try:
            data = bdecode(self._decrypt(data, cluster_key, iv))
        except Exception as exc:
            self.log.error("decrypt message from %s: %s", nodename, str(exc))
            self.blacklist(sender_id)
            return None, None
        self.blacklist_clear(sender_id)
        try:
            return nodename, json.loads(data)
        except ValueError as exc:
            return nodename, data

    def encrypt(self, data, cluster_name=None, secret=None, encode=True):
        """
        Encrypt and return data in a wrapping structure.
        """
        if cluster_name is None:
            cluster_name = self.cluster_name
        if secret is None:
            cluster_key = self.cluster_key
        else:
            cluster_key = secret
        if cluster_key is None:
            return
        iv = self.gen_iv()
        message = {
            "clustername": cluster_name,
            "nodename": rcEnv.nodename,
            "iv": bdecode(base64.urlsafe_b64encode(iv)),
            "data": bdecode(
                base64.urlsafe_b64encode(
                    self._encrypt(json.dumps(data), cluster_key, iv)
                )
            ),
        }
        if encode:
            return (json.dumps(message)+'\0').encode()
        return json.dumps(message)

    def blacklisted(self, sender_id):
        """
        Return True if the sender's problem count is above threshold.
        Else, return False.
        """
        if sender_id is None:
            return False
        sender_id = str(sender_id)
        with BLACKLIST_LOCK:
            count = BLACKLIST.get(sender_id, 0)
        if count > BLACKLIST_THRESHOLD:
            self.log.warning("received a message from blacklisted sender %s",
                             sender_id)
            return True
        return False

    def blacklist(self, sender_id):
        """
        Increment the sender's problem count in the blacklist.
        """
        if sender_id is None:
            return
        sender_id = str(sender_id)
        with BLACKLIST_LOCK:
            count = BLACKLIST.get(sender_id, 0)
            if sender_id in BLACKLIST:
                BLACKLIST[sender_id] += 1
                if count == BLACKLIST_THRESHOLD:
                    self.log.warning("sender %s blacklisted")
            else:
                BLACKLIST[sender_id] = 1

    def blacklist_clear(self, sender_id=None):
        """
        Clear the senders blacklist.
        """
        global BLACKLIST
        if sender_id is None and BLACKLIST == {}:
            return
        with BLACKLIST_LOCK:
            if sender_id is None:
                BLACKLIST = {}
                self.log.info("blacklist cleared")
            elif sender_id in BLACKLIST:
                del BLACKLIST[sender_id]
                self.log.info("sender %s removed from blacklist")

    @staticmethod
    def get_blacklist():
        """
        Return the senders blacklist.
        """
        with BLACKLIST_LOCK:
            return dict(BLACKLIST)

    def get_listener_info(self, nodename):
        """
        Get the listener address and port from node.conf.
        """
        if hasattr(self, "get_node"):
            config = self.get_node().config
        else:
            config = self.config
        if nodename == rcEnv.nodename:
            if not config.has_section("listener"):
                return "127.0.0.1", rcEnv.listener_port
            if config.has_option("listener", "addr@"+nodename):
                addr = config.get("listener", "addr@"+nodename)
            elif config.has_option("listener", "addr"):
                addr = config.get("listener", "addr")
            else:
                addr = "127.0.0.1"
            if config.has_option("listener", "port@"+nodename):
                port = config.getint("listener", "port@"+nodename)
            elif config.has_option("listener", "port"):
                port = config.getint("listener", "port")
            else:
                port = rcEnv.listener_port
        else:
            if not config.has_section("listener"):
                return nodename, rcEnv.listener_port
            if config.has_option("listener", "addr@"+nodename):
                addr = config.get("listener", "addr@"+nodename)
            else:
                addr = nodename
            if config.has_option("listener", "port@"+nodename):
                port = config.getint("listener", "port@"+nodename)
            elif config.has_option("listener", "port"):
                port = config.getint("listener", "port")
            else:
                port = rcEnv.listener_port
        return addr, port

    def recv_message(self, sock, cluster_name=None, secret=None, use_select=True, encrypted=True):
        """
        Receive, decrypt and return a message from a socket.
        """
        sock.setblocking(0)
        chunks = []
        while True:
            if use_select:
                ready = select.select([sock], [], [sock], 60)
                if ready[0]:
                    chunk = sock.recv(1)
                else:
                    break
                if ready[2]:
                    break
            else:
                chunk = sock.recv(1)
            if chunk:
                chunks.append(chunk)
            if not chunk or chunk == b"\x00":
                break
        if sys.version_info[0] >= 3:
            data = b"".join(chunks)
        else:
            data = "".join(chunks)
        if len(data) == 0:
            return
        if encrypted:
            nodename, data = self.decrypt(data, cluster_name=cluster_name,
                                          secret=secret)
        else:
            data = self.msg_decode(data)
        return data

    def socket_parms(self, nodename):
        data = Storage()
        if nodename == rcEnv.nodename and os.name != "nt":
            data.af = socket.AF_UNIX
            data.to = rcEnv.paths.lsnruxsock
            data.to_s = rcEnv.paths.lsnruxsock
            data.encrypted = False
        else:
            addr, port = self.get_listener_info(nodename)
            data.af = socket.AF_INET
            data.to = (addr, port)
            data.to_s = "%s:%d" % (addr, port)
            data.encrypted = True
        return data

    def daemon_send(self, data, nodename=None, with_result=True, silent=False,
                    cluster_name=None, secret=None, timeout=0):
        """
        Send a request to the daemon running on nodename and return the result
        fetched if with_result is set.
        """
        if nodename is None or nodename == "":
            nodename = rcEnv.nodename
        sp = self.socket_parms(nodename)
        try:
            sock = socket.socket(sp.af, socket.SOCK_STREAM)
            sock.settimeout(0.2)
            sock.connect(sp.to)
            if sp.encrypted:
                message = self.encrypt(data, cluster_name=cluster_name,
                                       secret=secret)
            else:
                message = self.msg_encode(data)
            if message is None:
                return {"status": 1, "err": "failed to encrypt message"}
            sock.sendall(message)
            if with_result:
                elapsed = 0
                while True:
                    try:
                        return self.recv_message(sock, cluster_name=cluster_name, secret=secret, encrypted=sp.encrypted)
                    except socket.timeout:
                        if timeout > 0 and elapsed > timeout:
                            return {"status": 1, "err": "timeout"}
                        elapsed += 0.2
        except socket.error as exc:
            if not silent:
                self.log.error("daemon send to %s error: %s", sp.to_s, str(exc))
            return {"status": 1}
        finally:
            sock.close()
        return {"status": 0}

    def daemon_get_stream(self, data, nodename=None, cluster_name=None,
                          secret=None):
        """
        Send a request to the daemon running on nodename and yield the results
        fetched if with_result is set.
        """
        if nodename is None:
            nodename = rcEnv.nodename
        sp = self.socket_parms(nodename)
        try:
            sock = socket.socket(sp.af, socket.SOCK_STREAM)
            sock.settimeout(6.2)
            sock.connect(sp.to)
            if sp.encrypted:
                message = self.encrypt(data, cluster_name=cluster_name,
                                       secret=secret)
            else:
                message = self.msg_encode(data)
            if message is None:
                return
            sock.sendall(message)
            while True:
                data = self.recv_message(sock, cluster_name=cluster_name, secret=secret, encrypted=sp.encrypted)
                yield data
        except socket.error as exc:
            self.log.error("daemon send to %s error: %s", sp.to_s, str(exc))
        finally:
            sock.close()

    def daemon_get_streams(self, data, nodenames=None, cluster_name=None,
                           secret=None):
        """
        Send a request to the daemon running on nodename and yield the results
        fetched if with_result is set.
        """
        if nodenames is None:
            nodenames = [rcEnv.nodename]

        socks = {}
        for nodename in nodenames:
            sp = self.socket_parms(nodename)
            try:
                sock = socket.socket(sp.af, socket.SOCK_STREAM)
                sock.settimeout(6.2)
                sock.connect(sp.to)
                if sp.encrypted:
                    message = self.encrypt(data, cluster_name=cluster_name,
                                           secret=secret)
                else:
                    message = self.msg_encode(data)
                if message is None:
                    return
                sock.sendall(message)
                socks[sock] = sp
            except socket.error as exc:
                self.log.error("daemon send to %s error: %s", sp.to_s, str(exc))

        try:
            while True:
                ready_to_read, _, exceptionals = select.select(socks.keys(), [], socks, 1)
                for sock in ready_to_read:
                    data = self.recv_message(sock, cluster_name=cluster_name, secret=secret, use_select=False, encrypted=socks[sock].encrypted)
                    if data is None:
                        continue
                    yield data
                for sock in exceptionals:
                    del socks[sock]
                if len(socks) == 0:
                    break
        finally:
            for sock in socks:
                sock.close()

   0707010005c8b4000081b40000000000000000000000015ab2175400007058000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/compliance.py  from __future__ import print_function
from stat import *
import os
import sys
import re
import datetime
import json
import rcExceptions as ex
from rcGlobalEnv import rcEnv
from rcUtilities import is_exe, justcall, banner, is_string, fcache
from subprocess import *
from rcColor import color, colorize, formatter

comp_dir = os.path.join(rcEnv.paths.pathvar, 'compliance')

# ex: \x1b[37;44m\x1b[1mContact List\x1b[0m\n
regex = re.compile("\x1b\[([0-9]{1,3}(;[0-9]{1,3})*)?[m|K|G]", re.UNICODE)

class Module(object):
    pattern = '^S*[0-9]+-*%(name)s$'

    def __init__(self, name, autofix=False, moduleset=None):
        self.name = name
        self.moduleset = moduleset
        self.executable = None
        self.autofix = autofix
        self.python_link_d = os.path.dirname(sys.executable)
        self.ordering = 0

        dl = os.listdir(comp_dir)
        match = []
        for e in dl:
            if re.match(self.pattern%dict(name=name), e) is not None:
                match.append(e)
        #if len(match) == 0:
        #    raise ex.excInitError('module %s not found in %s'%(name, comp_dir))
        if len(match) > 1:
            raise ex.excError('module %s matches too many entries in %s'%(name,
                              comp_dir))
        if len(match) == 1:
            self.init_module_exe(match[0])

    def init_module_exe(self, fpath):
        base = fpath
        if base[0] == 'S':
            base = base[1:]
        for i, c in enumerate(base):
            if not c.isdigit():
               break
        self.ordering = int(base[0:i])
        regex2 = re.compile("^S*[0-9]+-*", re.UNICODE)
        self.name = regex2.sub("", fpath)

        locations = []
        locations.append(os.path.join(comp_dir, fpath))
        locations.append(os.path.join(locations[0], 'main'))
        locations.append(os.path.join(locations[0], 'scripts', 'main'))

        for loc in locations:
            if not os.path.exists(loc):
                continue
            statinfo = os.stat(loc)
            mode = statinfo[ST_MODE]
            if statinfo.st_uid != 0 or statinfo.st_gid not in (0,2,3,4):
                raise ex.excError('%s is not owned by root. security hazard.'%(loc))
            if not S_ISREG(mode):
                continue
            if not is_exe(loc):
                mode |= S_IXUSR
                os.chmod(loc, mode)
            self.executable = loc

    def __str__(self):
        a = []
        a.append("name: %s"%self.name)
        a.append("ordering: %d"%self.ordering)
        a.append("executable: %s"%self.executable)
        return '\n'.join(a)


    def strip_unprintable(self, s):
        s = regex.sub('', s)
        if sys.version_info[0] >= 3:
            return s
        else:
            return s.decode('utf8', 'ignore')

    def log_action(self, out, ret, action):
        vals = [rcEnv.nodename,
                self.name,
                str(ret),
                self.strip_unprintable(out),
                action,
                self.rset_md5]
        if self.context.svc:
            vals.append(self.context.svc.svcname)
        else:
            vals.append("")
        self.context.action_log_vals.append(vals)

    def set_env_path(self):
        if self.python_link_d == sys.path[0]:
            return
        if rcEnv.sysname == "Windows":
            return
        if "PATH" in os.environ:
            os.environ["PATH"] = self.python_link_d + ":" + os.environ["PATH"]
        else:
            os.environ["PATH"] = self.python_link_d
        if rcEnv.paths.pathbin != "/usr/bin":
            os.environ["PATH"] = os.environ["PATH"] + ":" + rcEnv.paths.pathbin

    def set_locale(self):
        """
        Switch to an utf-8 locale
        """
        import locale
        locales = ["C.UTF-8", "en_US.UTF-8"]
        for loc in locales:
            try:
                locale.setlocale(locale.LC_ALL, loc)
                return
            except locale.Error:
                continue

    def reset_env(self):
        self.context.reset_env()

    def setup_env(self):
        os.environ.clear()
        os.environ.update(self.context.env_bkp)
        os.environ.update({
          "PYTHONIOENCODING": "utf-8",
          "OSVC_PYTHON": sys.executable,
          "OSVC_PATH_ETC": rcEnv.paths.pathetc,
          "OSVC_PATH_VAR": rcEnv.paths.pathvar,
          "OSVC_PATH_COMP": rcEnv.paths.pathcomp,
          "OSVC_PATH_TMP": rcEnv.paths.pathtmp,
          "OSVC_PATH_LOG": rcEnv.paths.pathlog,
          "OSVC_NODEMGR": rcEnv.paths.nodemgr,
          "OSVC_SVCMGR": rcEnv.paths.svcmgr,
        })
        self.set_locale()
        self.set_env_path()

        # add services env section keys, with values eval'ed on this node
        if self.context.svc:
            for key, val in self.context.svc.env_section_keys_evaluated().items():
                os.environ[self.context.format_rule_var("SVC_CONF_ENV_"+key.upper())] = self.context.format_rule_val(val)

        for rset in self.ruleset.values():
            if (rset["filter"] != "explicit attachment via moduleset" and \
                "matching non-public contextual ruleset shown via moduleset" not in rset["filter"]) or ( \
               self.moduleset in self.context.data["modset_rset_relations"]  and \
               rset['name'] in self.context.data["modset_rset_relations"][self.moduleset]
               ):
                for rule in rset['vars']:
                    var, val, var_class = self.context.parse_rule(rule)
                    os.environ[self.context.format_rule_var(var)] = self.context.format_rule_val(val)


    def action(self, action):
        self.print_bold(banner(self.name))

        if action not in ['check', 'fix', 'fixable', 'env']:
            print('action %s not supported')
            return 1

        if self.options.force:
            # short-circuit all pre and post action
            return self.do_action(action)

        if action == 'fix':
            if self.do_action('check') == 0:
                print('check passed, skip fix')
                return 0
            if self.do_action('fixable') not in (0, 2):
                print('not fixable, skip fix')
                return 1
            self.do_action('fix')
            r = self.do_action('check')
        elif action == 'check':
            r = self.do_action('check')
            if r == 1:
                self.do_action('fixable')
        elif action == 'fixable':
            r = self.do_action('fixable')
        elif action == 'env':
            r = self.do_env()
        return r

    def do_env(self):
        self.setup_env()
        for var in sorted(os.environ):
            print(var, "=", os.environ[var], sep="")
        self.reset_env()
        return 0

    def do_action(self, action):
        self.print_bold("ACTION:   %s"%action)
        if self.executable:
            ret, log = self.do_action_exe(action, [self.executable])
        else:
            ret, log = self.do_action_automodule(action)
        self.print_rcode(ret)
        self.log_action(log, ret, action)
        return ret

    def do_action_automodule(self, action):
        log = ''
        ret = 0

        self.setup_env()
        for rset in self.ruleset.values():
            if rset["name"] != self.moduleset:
                continue
            if "via moduleset" not in rset["filter"]:
                continue
            for rule in sorted(rset['vars'], key=lambda x: x[0]):
                var, val, var_class = self.context.parse_rule(rule)
                if var_class == "raw":
                    continue
                obj = self.get_obj(var_class)
                if obj is None:
                    err = color.RED + 'ERR: ' + color.END + "no compliance object found to handle class '%s' for rule '%s'" % (var_class, var)
                    log += err + "\n"
                    print(err, file=sys.stderr)
                    continue
                _ret, _log = self.do_action_exe(action, [obj, self.context.format_rule_var(var)])
                if _ret == 1:
                    ret = 1
                log += _log
                if action == "fix" and _ret not in (0, 2):
                    # stop at frist error in a 'fix' action
                    break

        self.reset_env()
        return ret, log

    def get_obj(self, var_class):
        import glob
        try:
            return glob.glob(os.path.join(comp_dir, "*", var_class+".py"))[0]
        except IndexError:
            return None

    def do_action_exe(self, action, executable):
        cmd = executable + [action]
        log = ''

        import tempfile
        import time
        fo = tempfile.NamedTemporaryFile()
        fe = tempfile.NamedTemporaryFile()
        _fo = None
        _fe = None

        def poll_out():
            fop = _fo.tell()
            line = _fo.readline()
            if not line:
                _fo.seek(fop)
                return None
            sys.stdout.write(line)
            sys.stdout.flush()
            return line

        def poll_err():
            fep = _fe.tell()
            line = _fe.readline()
            if not line:
                _fe.seek(fep)
                return None
            _line = color.RED + 'ERR: ' + color.END + line
            line = 'ERR: '+line
            sys.stdout.write(_line)
            sys.stdout.flush()
            return line

        def poll_pipes(log):
            i = 0
            while True:
                o = poll_out()
                e = poll_err()
                if o is not None:
                    log += o
                if e is not None:
                    log += e
                if o is None and e is None:
                    break
            return log

        try:
            self.setup_env()
            p = Popen(cmd, stdout=fo, stderr=fe, env=os.environ)
            _fo = open(fo.name, 'r')
            _fe = open(fe.name, 'r')
            while True:
                time.sleep(0.1)
                log = poll_pipes(log)
                if p.poll() != None:
                    log = poll_pipes(log)
                    break
        except OSError as e:
            if _fo is not None:
                _fo.close()
            if _fe is not None:
                _fe.close()
            fo.close()
            fe.close()
            if e.errno == 2:
                raise ex.excError("%s execution error (File not found or bad interpreter)"%cmd[0])
            elif e.errno == 8:
                raise ex.excError("%s execution error (Exec format error)"%cmd[0])
            else:
                raise
        fo.close()
        fe.close()
        _fo.close()
        _fe.close()
        self.reset_env()
        return p.returncode, log

    def print_bold(self, s):
        print(colorize(s, color.BOLD))

    def print_rcode(self, r):
        buff = "STATUS:   "
        if r == 1:
            buff += colorize("nok", color.RED)
        elif r == 0:
            buff += colorize("ok", color.GREEN)
        elif r == 2:
            buff += "n/a"
        else:
            buff += "%d" % r
        print(buff)

    def env(self):
        return self.action('env')

    def check(self):
        return self.action('check')

    def fix(self):
        return self.action('fix')

    def fixable(self):
        return self.action('fixable')

class Compliance(object):
    def __init__(self, o=None):
        if hasattr(o, "svcname"):
            self.svc = o
            self.node = o.node
        else:
            self.svc = None
            self.node = o
        self.options = o.options
        self.module_o = {}
        self.module = []
        self.updatecomp = False
        self.moduleset = None
        self.data = None
        self.action_log_vals = []
        self.action_log_vars = [
          'run_nodename',
          'run_module',
          'run_status',
          'run_log',
          'run_action',
          'rset_md5',
          'run_svcname']
        self.env_bkp = os.environ.copy()
        self.ordered_module = []

    def set_rset_md5(self):
        self.rset_md5 = ""
        rset = self.ruleset.get("osvc_collector")
        if rset is None:
            return
        for rule in rset["vars"]:
            var, val, var_class = self.parse_rule(rule)
            if var == "ruleset_md5":
                self.rset_md5 = val
                break

    def parse_rule(self, var):
        if len(var) == 2:
            return var[0], var[1], "raw"
        else:
            return var

    def setup_env(self):
        for rset in self.ruleset.values():
            for rule in rset['vars']:
                var, val, var_class = self.parse_rule(rule)
                os.environ[self.format_rule_var(var)] = self.format_rule_val(val)

    def reset_env(self):
        os.environ.clear()
        os.environ.update(self.env_bkp)

    def compliance_auto(self):
        if self.updatecomp and self.svc is None:
            self.node.updatecomp()
        self.do_auto()

    def compliance_env(self):
        self.do_run('env')

    def compliance_check(self):
        self.do_checks()

    def __iadd__(self, o):
        self.module_o[o.name] = o
        o.ruleset = self.ruleset
        o.options = self.options
        o.collector = self.node.collector
        o.context = self
        o.rset_md5 = self.rset_md5
        return self

    def print_bold(self, s):
        print(colorize(s, color.BOLD))

    def expand_modulesets(self, modulesets):
        l = []

        def recurse(ms):
            l.append(ms)
            if ms not in self.data["modset_relations"]:
                return
            for _ms in self.data["modset_relations"][ms]:
                recurse(_ms)

        for ms in modulesets:
            recurse(ms)

        return l

    def init(self):
        if self.options.moduleset != "" and self.options.module != "":
            raise ex.excError('--moduleset and --module are exclusive')

        if len(self.options.moduleset) != "" and \
           hasattr(self.options, "attach") and self.options.attach:
            self._compliance_attach_moduleset(self.options.moduleset.split(','))

        if self.data is None:
            try:
                self.data = self.get_comp_data()
            except Exception as e:
                raise ex.excError(str(e))
            if self.data is None:
                raise ex.excError("could not fetch compliance data from the collector")
            if "ret" in self.data and self.data["ret"] == 1:
                if "msg" in self.data:
                    raise ex.excError(self.data["msg"])
                raise ex.excError("could not fetch compliance data from the collector")
            modulesets = []
            if self.options.moduleset != "":
                # purge unspecified modulesets
                modulesets = self.options.moduleset.split(',')
                modulesets = self.expand_modulesets(modulesets)
                for ms in list(self.data["modulesets"].keys()):
                    if ms not in modulesets:
                        del(self.data["modulesets"][ms])
            elif self.options.module != "":
                # purge unspecified modules
                modules = self.options.module.split(',')
                for ms, data in self.data["modulesets"].items():
                    n = len(data)
                    for i in sorted(range(n), reverse=True):
                        module, autofix = data[i]
                        if module not in modules:
                            del(self.data["modulesets"][ms][i])
                for module in modules:
                    in_modsets = []
                    for ms, data in self.data["modulesets"].items():
                        for _module, autofix in data:
                            if module == _module:
                               in_modsets.append(ms)
                    if len(in_modsets) == 0:
                        print("module %s not found in any attached moduleset" % module)
                    elif len(in_modsets) > 1:
                        raise ex.excError("module %s found in multiple attached moduleset (%s). Use --moduleset instead of --module to clear the ambiguity" % (module, ', '.join(in_modsets)))

        self.module = self.merge_moduleset_modules()
        self.ruleset = self.data['rulesets']
        self.set_rset_md5()

        if not os.path.exists(comp_dir):
            os.makedirs(comp_dir, 0o755)

        for module, autofix, moduleset in self.module:
            try:
                self += Module(module, autofix, moduleset)
            except ex.excInitError as e:
                print(e, file=sys.stderr)

        self.ordered_module = list(self.module_o.keys())
        self.ordered_module.sort(key=lambda x: self.module_o[x].ordering)

    def __str__(self):
        print(banner('run context'))
        a = []
        a.append('modules:')
        for m in self.ordered_module:
            a.append(' %0.2d %s'%(self.module_o[m].ordering, m))
        a.append(self.str_ruleset())
        return '\n'.join(a)

    def format_rule_var(self, var):
        var = var.upper().replace('-', '_').replace(' ', '_').replace('.','_')
        var = '_'.join(('OSVC_COMP', var))
        return var

    def format_rule_val(self, val):
        if is_string(val):
            try:
                tmp = json.loads(val)
                val = json.dumps(tmp)
            except Exception as e:
                pass
            if sys.version_info[0] < 3:
                val = val.encode("utf-8")
        else:
            val = str(val)
        return val

    def get_moduleset(self):
        if self.svc:
            moduleset = self.node.collector.call('comp_get_svc_data_moduleset', self.svc.svcname)
        else:
            moduleset = self.node.collector.call('comp_get_data_moduleset')
        if moduleset is None:
            raise ex.excError('could not fetch moduleset')
        return moduleset

    def get_ruleset(self):
        if hasattr(self.options, 'ruleset') and \
           len(self.options.ruleset) > 0:
            return self.get_ruleset_md5(self.options.ruleset)
        return self.get_current_ruleset()

    def get_current_ruleset(self):
        if self.svc:
            ruleset = self.node.collector.call('comp_get_svc_ruleset', self.svc.svcname)
        else:
            ruleset = self.node.collector.call('comp_get_ruleset')
        if ruleset is None:
            raise ex.excError('could not fetch ruleset')
        return ruleset

    def get_ruleset_md5(self, rset_md5):
        ruleset = self.node.collector.call('comp_get_ruleset_md5', rset_md5)
        if ruleset is None:
            raise ex.excError('could not fetch ruleset')
        return ruleset

    def str_ruleset(self):
        a = []
        a.append('rules:')
        for rset in self.ruleset.values():
            if len(rset['filter']) == 0:
                a.append(' %s'%rset['name'])
            else:
                a.append(' %s (%s)'%(rset['name'],rset['filter']))
            for rule in rset['vars']:
                var, val, var_class = self.parse_rule(rule)
                val = self.format_rule_val(val)
                if ' ' in val:
                    val = repr(val)
                a.append('  %s=%s'%(self.format_rule_var(var), val))
        return '\n'.join(a)

    @fcache
    def get_comp_data(self):
        if self.svc:
            return self.node.collector.call('comp_get_svc_data',
                                            self.svc.svcname,
                                            modulesets=self.options.moduleset.split(','))
        else:
            return self.node.collector.call('comp_get_data',
                                            modulesets=self.options.moduleset.split(','))

    def merge_moduleset_modules(self):
        l = []
        for ms, data in self.data['modulesets'].items():
            for module, autofix in data:
                if (module, autofix) not in l:
                    l.append((module, autofix, ms))
                elif autofix and (module, False, ms) in l:
                    l.remove((module, False, ms))
                    l.append((module, True, ms))
        return l

    def digest_errors(self, err):
        passed = [m for m in err if err[m] == 0]
        errors = [m for m in err if err[m] == 1]
        na = [m for m in err if err[m] == 2]

        n_passed = len(passed)
        n_errors = len(errors)
        n_na = len(na)

        def _s(n):
            if n > 1:
                return 's'
            else:
                return ''

        def modules(l):
            if len(l) == 0:
                return ''
            return '\n%s'%'\n'.join(map(lambda x: ' '+x, l))

        self.print_bold(banner("digest"))
        print("%d n/a%s"%(n_na, modules(na)))
        print("%d passed%s"%(n_passed, modules(passed)))
        print("%d error%s%s"%(n_errors, _s(n_errors), modules(errors)))

        if len(errors) > 0:
            return 1
        return 0

    def compliance_show_moduleset(self):
        def recurse(ms, depth=0):
            prefix=" "*depth
            print(prefix+ms+':')
            if ms not in data["modulesets"]:
                print(prefix+" (no modules)")
                return
            for module, autofix in data["modulesets"][ms]:
                if autofix:
                    s = " (autofix)"
                else:
                    s = ""
                print(prefix+' %s%s' % (module, s))
            if ms in data["modset_relations"]:
                for _ms in data["modset_relations"][ms]:
                    recurse(_ms, depth+1)

        try:
            data = self.get_moduleset()
        except Exception as e:
            print(e, file=sys.stderr)
            return 1
        if "ret" in data and data["ret"] == 1:
            if "msg" in data:
                print(data["msg"], file=sys.stderr)
            return 1
        if "root_modulesets" not in data:
            print("(none)")
            return 0
        for ms in data["root_modulesets"]:
            recurse(ms)

    def compliance_show_ruleset(self):
        self.ruleset = self.get_ruleset()
        print(self.str_ruleset())

    def do_run(self, action):
        err = {}
        self.init()
        start = datetime.datetime.now()
        for module in self.ordered_module:
            _action = action
            if action == "auto":
                if self.module_o[module].autofix:
                    _action = "fix"
                else:
                    _action = "check"
            err[module] = getattr(self.module_o[module], _action)()
        if action == "env":
            return 0
        r = self.digest_errors(err)
        end = datetime.datetime.now()
        print("total duration: %s"%str(end-start))
        self.node.collector.call('comp_log_actions', self.action_log_vars, self.action_log_vals)
        return r

    def do_auto(self):
        return self.do_run('auto')

    def do_checks(self):
        return self.do_run('check')

    def compliance_fix(self):
        return self.do_run('fix')

    def compliance_fixable(self):
        return self.do_run('fixable')

    def compliance_detach(self):
        did_something = False
        if hasattr(self.options, 'moduleset') and \
           len(self.options.moduleset) > 0:
            did_something = True
            self._compliance_detach_moduleset(self.options.moduleset.split(','))
        if hasattr(self.options, 'ruleset') and \
           len(self.options.ruleset) > 0:
            did_something = True
            self._compliance_detach_ruleset(self.options.ruleset.split(','))
        if not did_something:
            raise ex.excError('no moduleset nor ruleset specified. use --moduleset and/or --ruleset')

    def compliance_attach(self):
        did_something = False
        if hasattr(self.options, 'moduleset') and \
           len(self.options.moduleset) > 0:
            did_something = True
            self._compliance_attach_moduleset(self.options.moduleset.split(','))
        if hasattr(self.options, 'ruleset') and \
           len(self.options.ruleset) > 0:
            did_something = True
            self._compliance_attach_ruleset(self.options.ruleset.split(','))
        if not did_something:
            raise ex.excError('no moduleset nor ruleset specified. use --moduleset and/or --ruleset')

    def _compliance_attach_moduleset(self, modulesets):
        err = False
        for moduleset in modulesets:
            if self.svc:
                d = self.node.collector.call('comp_attach_svc_moduleset', self.svc.svcname, moduleset)
            else:
                d = self.node.collector.call('comp_attach_moduleset', moduleset)
            if d is None:
                print("Failed to attach '%s' moduleset. The collector may not be reachable." % moduleset, file=sys.stderr)
                err = True
                continue
            if not d.get("status", True) or d.get("ret"):
                err = True
            print(d['msg'])
        if err:
            raise ex.excError()

    def _compliance_detach_moduleset(self, modulesets):
        err = False
        for moduleset in modulesets:
            if self.svc:
                d = self.node.collector.call('comp_detach_svc_moduleset', self.svc.svcname, moduleset)
            else:
                d = self.node.collector.call('comp_detach_moduleset', moduleset)
            if d is None:
                print("Failed to detach '%s' moduleset. The collector may not be reachable." % moduleset, file=sys.stderr)
                err = True
                continue
            if not d.get("status", True) or d.get("ret"):
                err = True
            print(d['msg'])
        if err:
            raise ex.excError()

    def _compliance_attach_ruleset(self, rulesets):
        err = False
        for ruleset in rulesets:
            if self.svc:
                d = self.node.collector.call('comp_attach_svc_ruleset', self.svc.svcname, ruleset)
            else:
                d = self.node.collector.call('comp_attach_ruleset', ruleset)
            if d is None:
                print("Failed to attach '%s' ruleset. The collector may not be reachable." % ruleset, file=sys.stderr)
                err = True
                continue
            if not d.get("status", True) or d.get("ret"):
                err = True
            print(d['msg'])
        if err:
            raise ex.excError()

    def _compliance_detach_ruleset(self, rulesets):
        err = False
        for ruleset in rulesets:
            if self.svc:
                d = self.node.collector.call('comp_detach_svc_ruleset', self.svc.svcname, ruleset)
            else:
                d = self.node.collector.call('comp_detach_ruleset', ruleset)
            if d is None:
                print("Failed to detach '%s' ruleset. The collector may not be reachable." % ruleset, file=sys.stderr)
                err = True
                continue
            if not d.get("status", True) or d.get("ret"):
                err = True
            print(d['msg'])
        if err:
            raise ex.excError()

    @formatter
    def compliance_show_status(self):
        return self._compliance_show_status()

    def _compliance_show_status(self):
        args = ['comp_show_status']
        if self.svc:
           args.append(self.svc.svcname)
        else:
           args.append('')
        if hasattr(self.options, 'module') and \
           len(self.options.module) > 0:
            args.append(self.options.module)
        l = self.node.collector.call(*args)
        if l is None:
            return
        return l

    def compliance_list_ruleset(self):
        if not hasattr(self.options, 'ruleset') or \
           len(self.options.ruleset) == 0:
            l = self.node.collector.call('comp_list_ruleset')
        else:
            l = self.node.collector.call('comp_list_ruleset', self.options.ruleset)
        if l is None:
            return
        print('\n'.join(l))

    def compliance_list_moduleset(self):
        if not hasattr(self.options, 'moduleset') or \
           len(self.options.moduleset) == 0:
            l = self.node.collector.call('comp_list_moduleset')
        else:
            l = self.node.collector.call('comp_list_moduleset', self.options.moduleset)
        if l is None:
            return
        print('\n'.join(l))

    def compliance_list_module(self):
        import glob
        regex2 = re.compile("^S*[0-9]+-*", re.UNICODE)
        for path in glob.glob(os.path.join(comp_dir, '*')):
            name = regex2.sub("", os.path.basename(path))
            try:
                m = Module(name)
                print(m.name)
            except:
                continue


0707010005c8b5000081b40000000000000000000000015ab2175400001a4a000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/converters.py  """
Converters, used by arguments and config file parsers.
"""
import re
import shlex

try:
    NUMERIC_TYPES = (int, float, long)
except:
    NUMERIC_TYPES = (int, float)

def convert_shlex(s):
    return shlex.split(s)

def convert_integer(s):
    """
    Return <s> cast to int.
    """
    return int(s)

def convert_list(s):
    """
    Return a list object from the <s>.
    Consider <s> is white-space delimited.
    """
    if s is None:
        return []
    if isinstance(s, list):
        return s
    return s.split()

def convert_list_lower(s):
    """
    Return convert_list with members converted to lowercase.
    """
    if s is None:
        return []
    if isinstance(s, list):
        return s
    return [member.lower() for member in convert_list(s)]

def convert_list_comma(s):
    """
    Return a list object from the <s>.
    Consider <s> is comma delimited.
    """
    if s is None:
        return []
    if isinstance(s, list):
        return s
    return re.split("\s*,\s*", s.strip())

def convert_set(s):
    """
    Return convert_list result cast to a set.
    """
    if isinstance(s, set):
        return s
    return set(convert_list(s))

def convert_set_comma(s):
    """
    Return convert_list_comma result cast to a set.
    """
    if isinstance(s, set):
        return s
    return set(convert_list_comma(s))

def convert_boolean(s):
    """
    Return a boolean from <s>.
    """
    true_vals = (
        "yes",
        "y",
        "true",
        "t",
        "1"
    )
    false_vals = (
        "no",
        "n",
        "false",
        "f",
        "0",
        "0.0",
        "",
        "none",
        "[]",
        "{}"
    )
    s = str(s).lower()
    if s in true_vals:
        return True
    if s in false_vals:
        return False
    raise ValueError('convert boolean error: ' + s)

def convert_duration(s, _to="s", _from="s"):
    """
    Convert a string representation of a duration to seconds.
    Supported units (case insensitive):
      w: week
      d: day
      h: hour
      m: minute
      s: second
    Example:
      1w => 604800
      1d => 86400
      1h => 3600
      1h1m => 3660
      1h2s => 3602
      1 => 1
    """
    if s is None:
        raise ValueError("convert duration error: None is not a valid duration")

    units = {
        "w": 604800,
        "d": 86400,
        "h": 3600,
        "m": 60,
        "s": 1,
    }

    if _from not in units:
        raise ValueError("convert duration error: unsupported input unit %s" % _from)
    if _to not in units:
        raise ValueError("convert duration error: unsupported target unit %s" % _to)

    try:
        s = int(s)
        return s * units[_from] // units[_to]
    except ValueError:
        pass

    s = s.lower()
    duration = 0
    prev = 0
    for idx, unit in enumerate(s):
        if unit not in units:
            continue
        _duration = s[prev:idx]
        try:
            _duration = int(_duration)
        except ValueError:
            raise ValueError("convert duration error: invalid format %s at index %d" % (s, idx))
        duration += _duration * units[unit]
        prev = idx + 1

    return duration // units[_to]

def convert_size(s, _to='', _round=1, default_unit=''):
    """
    Return an integer from the <s> expression, converting to a pivot unit,
    then to the target unit specified by <_to>, and finally round to a
    <_round> lowest multiple.
    """
    if s is None:
        return
    if type(s) in NUMERIC_TYPES:
        s = str(s)
    elif re.match("[0-9]+%(FREE|VG|ORIGIN|PVS|)", s):
        # lvm2 size expressions or percentage
        return s
    l = ['', 'K', 'M', 'G', 'T', 'P', 'Z', 'E']
    s = s.strip().replace(",", ".")
    if len(s) == 0:
        return 0
    if s == '0':
        return 0
    size = s
    unit = ""
    for i, c in enumerate(s):
        if not c.isdigit() and c not in ('.', '-'):
            size = s[:i]
            unit = s[i:].strip()
            break
    if 'i' in unit:
        factor = 1000
    else:
        factor = 1024
    if len(unit) > 0:
        unit = unit[0].upper()
    else:
        unit = default_unit
    size = float(size)

    try:
        start_idx = l.index(unit)
    except:
        raise ValueError("convert size error: unsupported unit in %s" % s)

    for i in range(start_idx):
        size *= factor

    if 'i' in _to:
        factor = 1000
    else:
        factor = 1024
    if len(_to) > 0:
        unit = _to[0].upper()
    else:
        unit = ''

    if unit == 'B':
        unit = ''

    try:
        end_idx = l.index(unit)
    except:
        raise ValueError("convert size error: unsupported target unit %s" % unit)

    for i in range(end_idx):
        size /= factor

    size = int(size)
    d = size % _round
    if d > 0:
        size = (size // _round) * _round
    return size

def print_size(size, unit="MB", compact=False):
    unit = unit.upper()
    if unit.endswith("B"):
        suffix = "B"
        unit = unit.rstrip("B")
    else:
        suffix = ""
    if unit.endswith("I"):
        metric = "i"
        mult = 1000
        unit = unit.rstrip("I")
    else:
        metric = ""
        mult = 1024
    units = ['', 'K', 'M', 'G', 'T', 'E']
    units_index = {'':0, 'K':1, 'M':2, 'G':3, 'T':4, 'E':5}
    size = float(size)
    if unit not in units:
        raise ValueError("unsupported unit: %s" % unit)
    sep = "" if compact else " "
    suffix = "" if compact else suffix
    roundup = False
    for u in units[units_index[unit]:]:
        if size == 0:
            return "0"
        u = u.lower() if compact else u
        if size < 0.95 * mult:
            return '%d%s%s%s%s' % (size+1 if roundup else size, sep, u, metric, suffix)
        elif size < mult:
            roundup = True
        size = size/mult
    return '%d%s%s%s%s' % (size, sep, u, metric, suffix)

def convert_speed(s, _to='', _round=1, default_unit=''):
    try:
        s = s.rstrip(" /s")
    except AttributeError:
        pass
    size = convert_size(s, _to=_to, _round=_round, default_unit=default_unit)
    return size

def convert_speed_kps(s, _round=1):
    return convert_speed(s, _to="KB", _round=_round, default_unit='K')

if __name__ == "__main__":
    #print(convert_size("10000 KiB", _to='MiB', _round=3))
    #print(convert_size("10M", _to='', _round=4096))
    for s in (1, "1", "1w1d1h1m1s", "1d", "1d1w", "2m2s", "Ad", "1dd", "-1", "-1s"):
        try:
            print(s, "=>", convert_duration(s, _to="d"))
        except ValueError as exc:
            print(exc)
    for s in (3000, "3000 kb/s", "3000 k/s", "3000k"):
        try:
            print(s, "=>", convert_speed_kps(s))
        except ValueError as exc:
            print(exc)

  0707010005c8b6000081b40000000000000000000000015ab217540000a926000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/extconfig.py   from __future__ import print_function

import sys
import re
import os

import rcExceptions as ex
from converters import *
from rcUtilities import is_string, try_decode, read_cf, eval_expr, unset_lazy, \
                        lazy, unset_all_lazy
from rcGlobalEnv import rcEnv

class ExtConfig(object):
    @lazy
    def has_default_section(self):
        if hasattr(self, "svcname"):
            return True
        else:
            return False

    def unset(self):
        """
        The 'unset' action entrypoint.
        Verifies the --param and --value are set, and finally call the _unset
        internal method.
        """
        if self.options.kw:
            kw = self.options.kw
        elif self.options.param:
            kw = [self.options.param]
        else:
            kw = None
        if kw is None:
            print("no keyword specified. set --kw <keyword>", file=sys.stderr)
            return 1
        ret = 0
        for _kw in kw:
            ret += self.unset_one(_kw)
        return ret

    def unset_one(self, kw):
        elements = kw.split('.')
        if self.has_default_section and len(elements) == 1:
            elements.insert(0, "DEFAULT")
        elif len(elements) != 2:
            print("malformed parameter. format as 'section.key'",
                  file=sys.stderr)
            return 1
        section, option = elements
        if section in self.default_status_groups:
            err = 0
            for rid in [rid for rid in self.config.sections() if rid.startswith(section+"#")]:
                try:
                    self._unset(rid, option)
                except ex.excError as exc:
                    print(exc, file=sys.stderr)
                    err += 1
            return err
        else:
            try:
                self._unset(section, option)
                return 0
            except ex.excError as exc:
                print(exc, file=sys.stderr)
                return 1

    def _unset(self, section, option):
        """
        Delete an option in the service configuration file specified section.
        """
        lines = self._read_cf().splitlines()
        lines = self.unset_line(lines, section, option)
        try:
            self._write_cf(lines)
        except (IOError, OSError) as exc:
            raise ex.excError(str(exc))
        unset_all_lazy(self)
        if hasattr(self, "ref_cache"):
            delattr(self, "ref_cache")

    def unset_line(self, lines, section, option):
        section = "[%s]" % section
        need_write = False
        in_section = False
        for i, line in enumerate(lines):
            sline = line.strip()
            if sline == section:
                in_section = True
            elif in_section:
                if sline.startswith("["):
                    break
                elif "=" in sline:
                    elements = sline.split("=")
                    _option = elements[0].strip()
                    if option != _option:
                        continue
                    del lines[i]
                    need_write = True
                    while i < len(lines) and "=" not in lines[i] and \
                          not lines[i].strip().startswith("[") and \
                          lines[i].strip() != "":
                        del lines[i]

        if not in_section:
            raise ex.excError("section %s not found" % section)

        if not need_write:
            raise ex.excError("option '%s' not found in section %s" % (option, section))

        return lines

    def get(self):
        """
        The 'get' action entrypoint.
        Verifies the --param or --kw is set, set DEFAULT as section if no section was
        specified, and finally print,
        * the raw value if --eval is not set
        * the dereferenced and evaluated value if --eval is set
        """
        if self.options.kw and len(self.options.kw) == 1:
            kw = self.options.kw[0]
        elif self.options.param:
            kw = self.options.param
        else:
            kw = None
        try:
            print(self._get(kw, self.options.eval))
        except ex.OptNotFound as exc:
            print(exc.default)
        except ex.RequiredOptNotFound as exc:
            return 1
        except ex.excError as exc:
            print(exc, file=sys.stderr)
            return 1
        except Exception:
            return 1
        return 0

    def _get(self, param=None, evaluate=False):
        """
        Verifies the param is set, set DEFAULT as section if no section was
        specified, and finally return,
        * the raw value if evaluate is False
        * the dereferenced and evaluated value if evaluate is True
        """
        if param is None:
            raise ex.excError("no parameter. set --param")
        elements = param.split('.')
        if self.has_default_section and len(elements) == 1:
            elements.insert(0, "DEFAULT")
        elif len(elements) != 2:
            raise ex.excError("malformed parameter. format as 'section.key'")
        section, option = elements
        if section == "DEFAULT" and not self.has_default_section:
            raise ex.excError("the DEFAULT section is not allowed in %s" % self.paths.cf)
        if not self.config.has_section(section):
            if section != 'DEFAULT' and self.has_default_section:
                raise ex.excError("section [%s] not found" % section)
            if not self.has_default_section:
                raise ex.excError("section [%s] not found" % section)
        if evaluate:
            return self.conf_get(section, option, "string", scope=True)
        else:
            return self.config.get(section, option)

    def set(self):
        """
        The 'set' action entrypoint.
        Verifies the --param and --value are set, set DEFAULT as section
        if no section was specified, and set the value using the internal
        _set() method.
        """
        if self.options.kw is not None:
            return self.set_multi(self.options.kw)
        else:
            return self.set_mono()

    def set_multi(self, kws):
        changes = []
        self.set_multi_cache = {}
        for kw in kws:
            if "=" not in kw:
                raise ex.excError("malformed kw expression: %s: no '='" % kw)
            keyword, value = kw.split("=", 1)
            if keyword[-1] == "-":
                op = "remove"
                keyword = keyword[:-1]
            elif keyword[-1] == "+":
                op = "add"
                keyword = keyword[:-1]
            else:
                op = "set"
            index = None
            if "[" in keyword:
                keyword, right = keyword.split("[", 1)
                if not right.endswith("]"):
                    raise ex.excError("malformed kw expression: %s: no trailing"
                                      " ']' at the end of keyword" % kw)
                try:
                    index = int(right[:-1])
                except ValueError:
                    raise ex.excError("malformed kw expression: %s: index is "
                                      "not integer" % kw)
            if "." in keyword and "#" not in keyword:
                # <group>.keyword[@<scope>] format => loop over all rids in group
                group = keyword.split(".")[0]
                if group in self.default_status_groups:
                    for rid in [rid for rid in self.config.sections() if rid.startswith(group+"#")]:
                        keyword = rid + keyword[keyword.index("."):]
                        changes.append(self.set_mangle(keyword, op, value, index))
                else:
                    # <section>.keyword[@<scope>]
                    changes.append(self.set_mangle(keyword, op, value, index))
            else:
                # <rid>.keyword[@<scope>]
                changes.append(self.set_mangle(keyword, op, value, index))
        self._set_multi(changes)

    def set_mono(self):
        self.set_multi_cache = {}
        if self.options.param is None:
            print("no parameter. set --param", file=sys.stderr)
            return 1
        if self.options.value is None and \
           self.options.add is None and \
           self.options.remove is None:
            print("no value. set --value, --add or --remove", file=sys.stderr)
            return 1
        if self.options.add:
            op = "add"
            value = self.options.add
        elif self.options.remove:
            op = "remove"
            value = self.options.remove
        else:
            op = "set"
            value = self.options.value
        keyword = self.options.param
        index = self.options.index
        changes = []
        if "." in keyword and "#" not in keyword:
            # <group>.keyword[@<scope>] format => loop over all rids in group
            group = keyword.split(".")[0]
            if group in self.default_status_groups:
                for rid in [rid for rid in self.config.sections() if rid.startswith(group+"#")]:
                    keyword = rid + keyword[keyword.index("."):]
                    changes.append(self.set_mangle(keyword, op, value, index))
            else:
                # <section>.keyword[@<scope>]
                changes.append(self.set_mangle(keyword, op, value, index))
        else:
            # <rid>.keyword[@<scope>]
            changes.append(self.set_mangle(keyword, op, value, index))

        self._set_multi(changes)

    def set_mangle(self, keyword, op, value, index):
        def list_value(keyword):
            import rcConfigParser
            if keyword in self.set_multi_cache:
                return self.set_multi_cache[keyword].split()
            try:
                _value = self._get(keyword, self.options.eval).split()
            except (ex.excError, rcConfigParser.NoOptionError) as exc:
                _value = []
            return _value

        if op == "remove":
            _value = list_value(keyword)
            if value not in _value:
                return
            _value.remove(value)
            _value = " ".join(_value)
            self.set_multi_cache[keyword] = _value
        elif op == "add":
            _value = list_value(keyword)
            if value in _value:
                return
            index = index if index is not None else len(_value)
            _value.insert(index, value)
            _value = " ".join(_value)
            self.set_multi_cache[keyword] = _value
        else:
            _value = value
        elements = keyword.split('.')
        if self.has_default_section and len(elements) == 1:
            elements.insert(0, "DEFAULT")
        elif len(elements) != 2:
            raise ex.excError("malformed kw: format as 'section.key'")
        return elements[0], elements[1], _value

    def _set(self, section, option, value):
        self._set_multi([[section, option, value]])

    def _set_multi(self, changes):
        changed = False
        lines = self._read_cf().splitlines()
        for change in changes:
            if change is None:
                continue
            section, option, value = change
            lines = self.set_line(lines, section, option, value)
            changed = True
        if not changed:
            # all changes were None
            return
        try:
            self._write_cf(lines)
        except (IOError, OSError) as exc:
            raise ex.excError(str(exc))
        unset_all_lazy(self)
        if hasattr(self, "ref_cache"):
            delattr(self, "ref_cache")

    def set_line(self, lines, section, option, value):
        """
        Set <option> to <value> in <section> of the configuration file.
        """
        section = "[%s]" % section
        done = False
        in_section = False
        value = try_decode(value)

        for idx, line in enumerate(lines):
            sline = line.strip()
            if sline == section:
                in_section = True
            elif in_section:
                if sline.startswith("["):
                    if done:
                        # matching section done and new section begins
                        break
                    else:
                        # section found and parsed and no option => add option
                        section_idx = idx
                        while section_idx > 0 and lines[section_idx-1].strip() == "":
                            section_idx -= 1
                        lines.insert(section_idx, "%s = %s" % (option, value))
                        done = True
                        break
                elif "=" in sline:
                    elements = sline.split("=")
                    _option = elements[0].strip()

                    if option != _option:
                        continue

                    if done:
                        # option already set : remove dup
                        del lines[idx]
                        while idx < len(lines) and "=" not in lines[idx] and \
                              not lines[idx].strip().startswith("[") and \
                              lines[idx].strip() != "":
                            del lines[idx]
                        continue

                    _value = elements[1].strip()
                    section_idx = idx

                    while section_idx < len(lines)-1 and  \
                          "=" not in lines[section_idx+1] and \
                          not lines[section_idx+1].strip().startswith("["):
                        section_idx += 1
                        if lines[section_idx].strip() == "":
                            continue
                        _value += " %s" % lines[section_idx].strip()

                    if value.replace("\n", " ") == _value:
                        return lines

                    lines[idx] = "%s = %s" % (option, value)
                    section_idx = idx

                    while section_idx < len(lines)-1 and \
                          "=" not in lines[section_idx+1] and \
                          not lines[section_idx+1].strip().startswith("[") and \
                          lines[section_idx+1].strip() != "":
                        del lines[section_idx+1]

                    done = True

        if not done:
            while len(lines) > 0 and lines[-1].strip() == "":
                lines.pop()
            if not in_section:
                # section in last position and no option => add section
                lines.append("")
                lines.append(section)
            lines.append("%s = %s" % (option, value))

        return lines

    #########################################################################
    #
    # config helpers
    #
    #########################################################################
    def handle_reference(self, ref, scope=False, impersonate=None, config=None):
            if "[" in ref and ref.endswith("]"):
                i = ref.index("[")
                index = ref[i+1:-1]
                ref = ref[:i]
                index = int(self.handle_references(index, scope=scope,
                                                   impersonate=impersonate))
            else:
                index = None

            if ref[0] == "#":
                return_length = True
                _ref = ref[1:]
            else:
                return_length = False
                _ref = ref

            # hardcoded references
            if _ref == "nodename":
                val = rcEnv.nodename
            elif _ref == "short_nodename":
                val = rcEnv.nodename.split(".")[0]
            elif _ref == "svcname" and hasattr(self, "svcname"):
                val = self.svcname
            elif _ref == "short_svcname" and hasattr(self, "svcname"):
                val = self.svcname.split(".")[0]
            elif _ref == "clustername":
                if hasattr(self, "node"):
                    val = self.node.cluster_name
                else:
                    val = self.cluster_name
            elif _ref == "clusternodes":
                if hasattr(self, "node"):
                    val = " ".join(self.node.cluster_nodes)
                else:
                    val = " ".join(self.cluster_nodes)
            elif _ref == "clusterdrpnodes":
                if hasattr(self, "node"):
                    val = " ".join(self.node.cluster_drpnodes)
                else:
                    val = " ".join(self.cluster_drpnodes)
            elif _ref == "dns":
                if hasattr(self, "node"):
                    val = " ".join(self.node.dns)
                else:
                    val = " ".join(self.dns)
            elif _ref == "dnsnodes":
                if hasattr(self, "node"):
                    val = " ".join(self.node.dnsnodes)
                else:
                    val = " ".join(self.dnsnodes)
            elif _ref == "svcmgr":
                val = rcEnv.paths.svcmgr
            elif _ref == "nodemgr":
                val = rcEnv.paths.nodemgr
            elif _ref == "etc":
                val = rcEnv.paths.pathetc
            elif _ref == "var":
                val = rcEnv.paths.pathvar
            elif _ref == "dnsuxsockd":
                val = rcEnv.paths.dnsuxsockd
            elif _ref == "dnsuxsock":
                val = rcEnv.paths.dnsuxsock
            elif _ref.startswith("safe://"):
                if hasattr(self, "node"):
                    val = self.node.download_from_safe(_ref, svcname=self.svcname)
                else:
                    val = self.download_from_safe(_ref)
                val = val.decode()
            else:
                val = None

            if val is None:
                # use DEFAULT as the implicit section
                n_dots = ref.count(".")
                if n_dots == 0 and self.has_default_section:
                    _section = "DEFAULT"
                    _v = ref
                elif n_dots == 1:
                    _section, _v = ref.split(".")
                else:
                    raise ex.excError("%s: reference can have only one dot" % ref)

                if len(_section) == 0:
                    raise ex.excError("%s: reference section can not be empty" % ref)
                if len(_v) == 0:
                    raise ex.excError("%s: reference option can not be empty" % ref)

                if _v[0] == "#":
                    return_length = True
                    _v = _v[1:]
                else:
                    return_length = False

                val = self._handle_reference(ref, _section, _v, scope=scope,
                                             impersonate=impersonate,
                                             config=config)

                if val is None:
                    # deferred
                    return

            if return_length or index is not None:
                if is_string(val):
                    val = val.split()
                if return_length:
                    return str(len(val))
                if index is not None:
                    try:
                        return val[index]
                    except IndexError:
                        if _v in ("exposed_devs", "sub_devs", "base_devs"):
                            return
                        raise

            return val

    def _handle_reference(self, ref, _section, _v, scope=False,
                          impersonate=None, config=None):
        if config is None:
            config = self.config
        # give os env precedence over the env cf section
        if _section == "env" and _v.upper() in os.environ:
            return os.environ[_v.upper()]

        if _section == "node" and hasattr(self, "svcname"):
            # go fetch the reference in the node.conf [node] section
            if self.node is None:
                from node import Node
                self.node = Node()
            try:
                return self.node.config.get("node", _v)
            except Exception as exc:
                raise ex.excError("%s: unresolved reference (%s)"
                                  "" % (ref, str(exc)))

        if _section != "DEFAULT" and not config.has_section(_section):
            raise ex.excError("%s: section %s does not exist" % (ref, _section))

        # deferrable refs
        if hasattr(self, "svcname"):
            for dref in ("exposed_devs", "base_devs", "sub_devs"):
                if _v != dref:
                    continue
                try:
                    res = self.get_resource(_section)
                    devs = getattr(res, dref)()
                    return list(devs)
                except Exception as exc:
                    return

        try:
            return self.conf_get(_section, _v, "string", scope=scope,
                                 impersonate=impersonate, config=config)
        except ex.OptNotFound as exc:
            return exc.default
        except ex.RequiredOptNotFound:
            raise ex.excError("%s: unresolved reference (%s)"
                              "" % (ref, str(exc)))

        raise ex.excError("%s: unknown reference" % ref)

    def _handle_references(self, s, scope=False, impersonate=None, config=None):
        if not is_string(s):
            return s
        while True:
            m = re.search(r'{\w*[\w#][\w\.\[\]:\/]*}', s)
            if m is None:
                return s
            ref = m.group(0).strip("{}").lower()
            val = self.handle_reference(ref, scope=scope,
                                        impersonate=impersonate,
                                        config=config)
            if val is None:
                # deferred
                return
            s = s[:m.start()] + val + s[m.end():]

    @staticmethod
    def _handle_expressions(s):
        if not is_string(s):
            return s
        while True:
            m = re.search(r'\$\((.+)\)', s)
            if m is None:
                return s
            expr = m.group(1)
            try:
                val = eval_expr(expr)
            except TypeError as exc:
                raise ex.excError("invalid expression: %s: %s" % (expr, str(exc)))
            if m.start() == 0 and m.end() == len(s):
                # preserve the expression type
                return val
            s = s[:m.start()] + str(val) + s[m.end():]
            return s

    def handle_references(self, s, scope=False, impersonate=None, config=None):
        key = (s, scope, impersonate)
        if hasattr(self, "ref_cache") and self.ref_cache is not None \
           and key in self.ref_cache:
            return self.ref_cache[key]
        try:
            val = self._handle_references(s, scope=scope,
                                          impersonate=impersonate,
                                          config=config)
            val = self._handle_expressions(val)
            val = self._handle_references(val, scope=scope,
                                          impersonate=impersonate,
                                          config=config)
        except Exception as e:
            raise
            raise ex.excError("%s: reference evaluation failed: %s"
                              "" % (s, str(e)))
        if hasattr(self, "ref_cache") and self.ref_cache is not None and \
           val is not None:
            # don't cache lazy reference miss-evaluations
            self.ref_cache[key] = val
        return val

    def conf_get(self, s, o, t=None, scope=None, impersonate=None,
                 use_default=True, config=None, verbose=True):
        """
        Handle keyword and section deprecation.
        """
        section = s.split("#")[0]
        if section in self.kwdict.DEPRECATED_SECTIONS:
            section, rtype = self.kwdict.DEPRECATED_SECTIONS[section]
            fkey = ".".join((section, rtype, o))
        else:
            try:
                rtype = self.config.get(s, "type")
                fkey = ".".join((section, rtype, o))
            except Exception:
                if hasattr(self, "svcname") and section == "sync":
                    rtype = "rsync"
                    fkey = ".".join((section, rtype, o))
                else:
                    rtype = None
                    fkey = ".".join((section, o))

        deprecated_keywords = self.kwdict.REVERSE_DEPRECATED_KEYWORDS.get(fkey)
        if deprecated_keywords is not None and not isinstance(deprecated_keywords, list):
            deprecated_keywords = [deprecated_keywords]

        # 1st try: supported keyword
        try:
            return self._conf_get(s, o, t=t, scope=scope,
                                  impersonate=impersonate,
                                  use_default=use_default, config=config,
                                  section=section, rtype=rtype)
        except ex.RequiredOptNotFound:
            if deprecated_keywords is None:
                if verbose:
                    self.log.error("%s.%s is mandatory" % (s, o))
                raise
        except ex.OptNotFound:
            if deprecated_keywords is None:
                raise

        # 2nd try: deprecated keyword
        exc = None
        for deprecated_keyword in deprecated_keywords:
            try:
                return self._conf_get(s, deprecated_keyword, t=t, scope=scope,
                                      impersonate=impersonate,
                                      use_default=use_default, config=config,
                                      section=section, rtype=rtype)
            except ex.RequiredOptNotFound as exc:
                pass
        if exc:
            self.log.error("%s.%s is mandatory" % (s, o))
            raise exc

    def _conf_get(self, s, o, t=None, scope=None, impersonate=None,
                 use_default=True, config=None, section=None, rtype=None):
        """
        Get keyword properties and handle inheritance.
        """
        inheritance = "leaf"
        kwargs = {
            "default": None,
            "required": False,
            "deprecated": False,
            "impersonate": impersonate,
            "use_default": use_default,
            "config": config,
        }
        if s != "env":
            key = self.kwdict.KEYS[section].getkey(o, rtype)
            if key is None:
                if scope is None and t is None:
                    raise ValueError("%s.%s not found in the "
                                     "keywords dictionary" % (s, o))
                else:
                    # passing 't' and 'scope' skips KEYS validation.
                    # used for keywords not in KEYS.
                    pass
            else:
                kwargs["deprecated"] = (key.keyword != o)
                kwargs["required"] = key.required
                kwargs["default"] = key.default
                inheritance = key.inheritance
                if scope is None:
                    scope = key.at
                if t is None:
                    t = key.convert
        else:
            # env key are always string and scopable
            t = "string"
            scope = True

        if scope is None:
            scope = False
        if t is None:
            t = "string"

        kwargs["scope"] = scope
        kwargs["t"] = t

        # in order of probability
        if self.has_default_section:
            if inheritance == "leaf > head":
                return self.__conf_get(s, o, **kwargs)
            if inheritance == "leaf":
                kwargs["use_default"] = False
                return self.__conf_get(s, o, **kwargs)
            if inheritance == "head":
                return self.__conf_get("DEFAULT", o, **kwargs)
            if inheritance == "head > leaf":
                try:
                    return self.__conf_get("DEFAULT", o, **kwargs)
                except ex.OptNotFound:
                    kwargs["use_default"] = False
                    return self.__conf_get(s, o, **kwargs)
        else:
            return self.__conf_get(s, o, **kwargs)
        raise ex.excError("unknown inheritance value: %s" % str(inheritance))

    def __conf_get(self, s, o, t=None, scope=None, impersonate=None,
                   use_default=None, config=None, default=None, required=None,
                   deprecated=None):
        try:
            if not scope:
                val = self.conf_get_val_unscoped(s, o, use_default=use_default,
                                                 config=config)
            else:
                val = self.conf_get_val_scoped(s, o, use_default=use_default,
                                               config=config,
                                               impersonate=impersonate)
        except ex.OptNotFound as exc:
            if required:
                raise ex.RequiredOptNotFound
            else:
                exc.default = default
                raise exc

        try:
            val = self.handle_references(val, scope=scope,
                                         impersonate=impersonate,
                                         config=config)
        except ex.excError as exc:
            if o.startswith("pre_") or o.startswith("post_") or \
               o.startswith("blocking_"):
                pass
            else:
                raise

        if t in (None, "string"):
            return val
        return globals()["convert_"+t](val)

    def conf_get_val_unscoped(self, s, o, use_default=True, config=None):
        if config is None:
            config = self.config
        if config.has_option(s, o):
            return config.get(s, o)
        raise ex.OptNotFound("unscoped keyword %s.%s not found." % (s, o))

    def conf_has_option_scoped(self, s, o, impersonate=None, config=None, scope_order=None):
        """
        Handles the keyword scope_order property, at and impersonate
        """
        if config is None:
            config = self.config
        if impersonate is None:
            nodename = rcEnv.nodename
        else:
            nodename = impersonate

        if s != "DEFAULT" and not config.has_section(s):
            return

        if s == "DEFAULT":
            options = config.defaults().keys()
        else:
            options = config._sections[s].keys()

        candidates = [
            (o+"@"+nodename, True),
        ]
        if hasattr(self, "svcname"):
            candidates += [
                (o+"@nodes", nodename in self.nodes),
                (o+"@drpnodes", nodename in self.drpnodes),
                (o+"@encapnodes", nodename in self.encapnodes),
                (o+"@flex_primary", nodename == self.flex_primary),
                (o+"@drp_flex_primary", nodename == self.drp_flex_primary),
            ]
        else:
            candidates += [
                (o+"@nodes", nodename in self.cluster_nodes),
                (o+"@drpnodes", nodename in self.cluster_drpnodes),
            ]
        candidates += [
            (o, True),
        ]

        if scope_order == "head: generic > specific" and s == "DEFAULT":
            candidates.reverse()
        elif scope_order == "generic > specific":
            candidates.reverse()

        for option, condition in candidates:
            if option in options and condition:
                return option

    def conf_get_val_scoped(self, s, o, impersonate=None, use_default=True, config=None, scope_order=None):
        if config is None:
            config = self.config
        if impersonate is None:
            nodename = rcEnv.nodename
        else:
            nodename = impersonate

        option = self.conf_has_option_scoped(s, o, impersonate=impersonate,
                                             config=config,
                                             scope_order=scope_order)
        if option is None and (not self.has_default_section or not use_default):
            raise ex.OptNotFound("scoped keyword %s.%s not found." % (s, o))

        if option is None and use_default and self.has_default_section:
            if s != "DEFAULT":
                # fallback to default
                return self.conf_get_val_scoped("DEFAULT", o,
                                                impersonate=impersonate,
                                                config=config,
                                                scope_order=scope_order)
            else:
                raise ex.OptNotFound("scoped keyword %s.%s not found." % (s, o))

        try:
            val = config.get(s, option)
        except Exception as e:
            raise ex.excError("param %s.%s: %s"%(s, o, str(e)))

        return val

    def validate_config(self, path=None):
        """
        The validate config action entrypoint.
        """
        ret = self._validate_config(path=path)
        return ret["warnings"] + ret["errors"]

    def _validate_config(self, path=None):
        """
        The validate config core method.
        Returns a dict with the list of syntax warnings and errors.
        """
        try:
            import ConfigParser
        except ImportError:
            import configparser as ConfigParser

        ret = {
            "errors": 0,
            "warnings": 0,
        }

        if path is None:
            config = self.config
        else:
            try:
                config = read_cf(path)
            except ConfigParser.ParsingError:
                self.log.error("error parsing %s" % path)
                ret["errors"] += 1

        def check_scoping(key, section, option):
            """
            Verify the specified option scoping is allowed.
            """
            if not key.at and "@" in option:
                self.log.error("option %s.%s does not support scoping", section, option)
                return 1
            return 0

        def check_references(section, option):
            """
            Verify the specified option references.
            """
            value = config.get(section, option)
            try:
                value = self.handle_references(value, scope=True, config=config)
            except ex.excError as exc:
                if not option.startswith("pre_") and \
                   not option.startswith("post_") and \
                   not option.startswith("blocking_"):
                    self.log.error(str(exc))
                    return 1
            except Exception as exc:
                self.log.error(str(exc))
                return 1
            return 0

        def get_val(key, section, option):
            """
            Fetch the value and convert it to the expected type.
            """
            _option = option.split("@")[0]
            value = self.conf_get(section, _option, config=config)
            return value

        def check_candidates(key, section, option, value):
            """
            Verify the specified option value is in allowed candidates.
            """
            if not key.strict_candidates:
                return 0
            if key.candidates is None:
                return 0
            if isinstance(value, (list, tuple, set)):
                valid = len(set(value) - set(key.candidates)) == 0
            else:
                valid = value in key.candidates
            if not valid:
                if isinstance(key.candidates, (set, list, tuple)):
                    candidates = ", ".join([str(candidate) for candidate in key.candidates])
                else:
                    candidates = str(key.candidates)
                self.log.error("option %s.%s value %s is not in valid candidates: %s",
                               section, option, str(value), candidates)
                return 1
            return 0

        def check_known_option(key, section, option):
            """
            Verify the specified option scoping, references and that the value
            is in allowed candidates.
            """
            err = 0
            err += check_scoping(key, section, option)
            if check_references(section, option) != 0:
                err += 1
                return err
            try:
                value = get_val(key, section, option)
            except ValueError as exc:
                self.log.warning(str(exc))
                return 0
            except ex.OptNotFound:
                return 0
            err += check_candidates(key, section, option, value)
            return err

        def validate_default_options(config, ret):
            """
            Validate DEFAULT section options.
            """
            if not self.has_default_section:
                return ret
            for option in config.defaults():
                key = self.kwdict.KEYS.sections["DEFAULT"].getkey(option)
                if key is None:
                    found = False
                    # the option can be set in the DEFAULT section for the
                    # benefit of a resource section
                    for section in config.sections():
                        family = section.split("#")[0]
                        if family not in list(self.kwdict.KEYS.sections.keys()) + \
                           list(self.kwdict.DEPRECATED_SECTIONS.keys()):
                            continue
                        if family in self.kwdict.DEPRECATED_SECTIONS:
                            results = self.kwdict.DEPRECATED_SECTIONS[family]
                            family = results[0]
                        if self.kwdict.KEYS.sections[family].getkey(option) is not None:
                            found = True
                            break
                    if not found:
                        self.log.warning("ignored option DEFAULT.%s", option)
                        ret["warnings"] += 1
                else:
                    # here we know its a native DEFAULT option
                    ret["errors"] += check_known_option(key, "DEFAULT", option)
            return ret

        def validate_resources_options(config, ret):
            """
            Validate resource sections options.
            """
            for section in config.sections():
                if section == "env":
                    # the "env" section is not handled by a resource driver, and is
                    # unknown to the self.kwdict. Just ignore it.
                    continue
                family = section.split("#")[0]
                if config.has_option(section, "type"):
                    rtype = config.get(section, "type")
                else:
                    rtype = None
                if family not in list(self.kwdict.KEYS.sections.keys()) + list(self.kwdict.DEPRECATED_SECTIONS.keys()):
                    self.log.warning("ignored section %s", section)
                    ret["warnings"] += 1
                    continue
                if family in self.kwdict.DEPRECATED_SECTIONS:
                    self.log.warning("deprecated section prefix %s", family)
                    ret["warnings"] += 1
                    family, rtype = self.kwdict.DEPRECATED_SECTIONS[family]
                for option in config.options(section):
                    if option in config.defaults():
                        continue
                    key = self.kwdict.KEYS.sections[family].getkey(option, rtype=rtype)
                    if key is None:
                        key = self.kwdict.KEYS.sections[family].getkey(option)
                    if key is None:
                        self.log.warning("ignored option %s.%s%s", section,
                                         option, ", driver %s" % rtype if rtype else "")
                        ret["warnings"] += 1
                    else:
                        ret["errors"] += check_known_option(key, section, option)
            return ret

        def validate_build(path, ret):
            """
            Try a service build to catch errors missed in other tests.
            """
            if not hasattr(self, "svcname"):
                return ret
            from svcBuilder import build
            try:
                build(self.svcname, svcconf=path, node=self.node)
            except Exception as exc:
                self.log.error("the new configuration causes the following "
                               "build error: %s", str(exc))
                ret["errors"] += 1
            return ret

        ret = validate_default_options(config, ret)
        ret = validate_resources_options(config, ret)
        ret = validate_build(path, ret)

        return ret

    def _read_cf(self):
        """
        Return the service config file content.
        """
        if not os.path.exists(self.paths.cf):
            return ""
        import codecs
        with codecs.open(self.paths.cf, "r", "utf8") as ofile:
            buff = ofile.read()
        return buff

    def _write_cf(self, buff):
        """
        Truncate the service config file and write buff.
        """
        import codecs
        import tempfile
        import shutil
        if isinstance(buff, list):
            buff = "\n".join(buff) + "\n"
        if hasattr(self, "svcname"):
            prefix = self.svcname
        else:
            prefix = "node"
        ofile = tempfile.NamedTemporaryFile(delete=False, dir=rcEnv.paths.pathtmp, prefix=prefix)
        fpath = ofile.name
        os.chmod(fpath, 0o0640)
        ofile.close()
        with codecs.open(fpath, "w", "utf8") as ofile:
            ofile.write(buff)
            ofile.flush()
        shutil.move(fpath, self.paths.cf)

    def print_config_data(self, src_config=None, evaluate=False, impersonate=None):
        """
        Return a simple dict (OrderedDict if possible), fed with the
        service configuration sections and keys
        """
        try:
            from collections import OrderedDict
            best_dict = OrderedDict
        except ImportError:
            best_dict = dict
        data = best_dict()
        tmp = best_dict()
        if src_config is None:
            config = self.config
        else:
            config = src_config

        defaults = config.defaults()
        for key in defaults.keys():
            tmp[key] = defaults[key]

        data['DEFAULT'] = tmp
        config._defaults = {}

        sections = config.sections()
        for section in sections:
            options = config.options(section)
            tmpsection = best_dict()
            for option in options:
                if config.has_option(section, option):
                    tmpsection[option] = config.get(section, option)
            data[section] = tmpsection
        if src_config is None:
            unset_lazy(self, "config")
        if not evaluate:
            return data
        edata = {}
        for section, _data in data.items():
            edata[section] = {}
            keys = []
            for key in _data:
                key = key.split("@")[0]
                if key in edata[section]:
                    continue
                val = self.conf_get(section, key, impersonate=impersonate)
                # ensure the data is json-exportable
                if isinstance(val, set):
                    val = list(val)
                edata[section][key] = val
        return edata

  0707010005c8b7000081b40000000000000000000000015ab2175400003434000000660000000000000000000000000000002500000000root/usr/share/opensvc/lib/forest.py  # -*- coding: utf8 -*-
"""
Forest data representation module.
"""
from __future__ import print_function
from __future__ import unicode_literals

from textwrap import wrap

from rcUtilities import term_width
from rcColor import colorize, color

LAST_NODE = "`- "
NEXT_NODE = "|- "
CONT_NODE = "|  "
CONT_LAST_NODE = "   "

def forest(data, columns=1, separator="  ", widths=None):
    """
    Print a nested dict structure as a tree.
    Each node is considered tabular, with cells content aligned
    and wrapped.

    The widths parameter can used to set per-column min/max or exact widths:

    widths = [
        (0, 10),   # col1: min 0, max 10 chars
        None,      # col2: no constraints, auto detect
        10         # col3: exactly 10 chars
    ]

    Example:

    {
        "data": [
            {
                "text": "node1",
                "color": color.BOLD
            }
        ],
        "children": [
            {
                "data": [
                    {
                        "text": "node2"
                    },
                    {
                        "text": "foo",
                        "color": color.RED
                    }
                ],
                "children": [
                ]
            }
        ]
    }

    would be rendered as:

    node1
    `- node2 foo

    """
    def get_pads(data, columns, widths=None):
        """
        Analyse data length in data columns and return a list of columns length,
        with no regards to terminal width constraint.
        """
        def rpads(data, pads, depth=0, max_depth=0):
            """
            Recursion function.
            """
            if "data" in data and isinstance(data["data"], dict):
                data["data"] = [data["data"]]
            for idx, col in enumerate(data.get("data", [])):
                try:
                    _width = widths[idx]
                except IndexError:
                    _width = None
                if isinstance(_width, int):
                    pads[idx] = _width
                    continue
                if isinstance(col, dict):
                    col = [col]
                for fragment in col:
                    text = fragment.get("text", "")
                    if text is None:
                        width = 0
                    else:
                        width = len(text)
                    if width > pads[idx]:
                        pads[idx] = width
                        if isinstance(_width, (list, tuple)):
                            _min, _max = _width
                            if _min is not None and pads[idx] < _min:
                                pads[idx] = _min
                            if _max is not None and pads[idx] > _max:
                                pads[idx] = _max
            next_depth = depth + 1
            for child in data.get("children", []):
                pads, depth, max_depth = rpads(child, pads, next_depth, max_depth)
                if depth > max_depth:
                    max_depth = depth
            return pads, depth, max_depth
        if widths is None:
            widths = [None] * columns
        pads = [0] * columns
        pads, _, max_depth = rpads(data, pads)
        return pads, max_depth

    def adjust_pads(pads, columns, depth, separator):
        """
        Given the pads returned by get_pads(), distribute the term width to
        columns.
        """
        twidth = term_width() - 4
        max_prefix_len = depth * 3
        width = 0
        for pad in pads:
            width += pad
        width += (columns - 1) * len(separator)
        width += (depth - 1) * 3
        oversize = width - twidth
        if oversize <= 0:
            return pads
        avg_cwidth = (twidth - len(separator) * (columns - 1)) // columns
        n_oversize = 0
        for idx, pad in enumerate(pads):
            if avg_cwidth - pad < 0:
                n_oversize += 1
        remaining_width = twidth - max_prefix_len
        for idx, pad in enumerate(pads):
            if pad <= avg_cwidth:
                remaining_width -= pad + len(separator)
        for idx, pad in enumerate(pads):
            if pad > avg_cwidth:
                pads[idx] = remaining_width // n_oversize
        #print("columns:", columns)
        #print("twidth:", twidth)
        #print("avg_cwidth:", avg_cwidth)
        #print("n_oversize:", n_oversize)
        #print("remaining_width:", remaining_width)
        #print("pads:", pads)
        return pads

    def format_prefix(lasts, n_children, subnode_idx):
        """
        Return the forest markers as a string for a line.
        """
        if len(lasts) == 0:
            return ""
        buff = ""
        if subnode_idx == 0:
            # new node
            for last in lasts[:-1]:
                if last:
                    buff += CONT_LAST_NODE
                else:
                    buff += CONT_NODE
            if lasts[-1]:
                buff += LAST_NODE
            else:
                buff += NEXT_NODE
        else:
            # node continuation due to wrapping
            for last in lasts:
                if last:
                    buff += CONT_LAST_NODE
                else:
                    buff += CONT_NODE
            if n_children > 0:
                buff += CONT_NODE
            else:
                buff += CONT_LAST_NODE
        return buff

    def format_cell(text, width, textcolor, separator):
        """
        Format the table cell, happending the separator, coloring the text and
        applying the cell padding for alignment.
        """
        if text in ("", None):
            return " " * width + separator
        fmt = "%-"+str(width)+"s"
        cell = fmt % text
        if textcolor:
            cell = colorize(cell, textcolor)
        return cell + separator

    def wrapped_lines(text, width):
        """
        Return lines split by the text wrapper wrapping at <width>.
        """
        if width == 0:
            return []
        return wrap(
            text,
            initial_indent="",
            subsequent_indent="",
            width=width
        )

    def recurse(data, pads, depth, buff="", lasts=None):
        """
        Recurse the data and return the forest tree buffer string.
        """
        if lasts is None:
            lasts = []
        children = data.get("children", [])
        n_children = len(children)
        last_child = n_children - 1
        for subnode_idx, subnode in enumerate(data.get("data", [])):
            prefix = format_prefix(lasts, n_children, subnode_idx)
            prefix_len = len(prefix)
            buff += prefix
            for idx, col in enumerate(subnode):
                text = col.get("text", "")
                textcolor = col.get("color")
                width = pads[idx]
                if idx == 0:
                    # adjust for col0 alignment shifting due to the prefix
                    width += depth * 3 - prefix_len
                buff += format_cell(text, width, textcolor, separator)
            buff += "\n"
        for idx, child in enumerate(children):
            last = idx == last_child
            buff = recurse(child, pads, depth, buff, lasts=lasts+[last])
        return buff

    def wrap_data(data, pads):
        """
        Transform the data, applying the wrapping to each cell and reassembling
        the results in a tabular format.
        """
        _data = {
            "data": [],
            "children": [],
        }
        tmp = []
        max_lines = 0
        for idx, col in enumerate(data.get("data", [])):
            if isinstance(col, dict):
                col = [col]
            n_lines = 0
            lines = []
            for fragment in col:
                text = fragment.get("text", "")
                textcolor = fragment.get("color")
                if text is None:
                    text = ""
                lines += [(line, textcolor) for line in wrapped_lines(text, pads[idx])]
            n_lines += len(lines)
            tmp.append(lines)
            if n_lines > max_lines:
                max_lines = n_lines
        for idx in range(max_lines):
            __data = []
            for _idx in range(columns):
                try:
                    _tmp = tmp[_idx]
                except IndexError:
                    break
                try:
                    line, textcolor = _tmp[idx]
                except IndexError:
                    line = ""
                __data.append({
                    "text": line,
                    "color": textcolor
                })
            _data["data"].append(__data)

        children = data.get("children", [])
        for child in children:
            _data["children"].append(wrap_data(child, pads))
        return _data

    pads, depth = get_pads(data, columns, widths=widths)
    pads = adjust_pads(pads, columns, depth, separator)
    data = wrap_data(data, pads)
    #import json
    #print(json.dumps(data, indent=4))

    return recurse(data, pads, depth)

class Column(object):
    """
    The Forest Node Column object, offering a method to add extra phrases
    to the column.
    """
    def __init__(self, node=None, idx=0):
        self.idx = idx
        self.node = node

    def add_text(self, text="", color=None):
        """
        Add a phrase to this column.
        """
        self.node.node["data"][self.idx].append({
            "text": text,
            "color": color
        })

class Node(object):
    """
    The Forest Node object, offering methods to add columns to the node.
    """
    def __init__(self, forest, node_id):
        self.forest = forest
        self.node_id = node_id
        self.node = self.forest.get_node(node_id)

    def add_column(self, text="", color=None):
        """
        Add and return a column to the node with text and color.
        Extra phrases can be added through the returned Column object.
        """
        if "data" not in self.node:
            self.node["data"] = []
        self.node["data"].append([{
            "text": text,
            "color": color
        }])
        columns = len(self.node["data"])
        if columns > self.forest.columns:
            self.forest.columns = columns
        return Column(node=self, idx=columns-1)

    def add_node(self):
        """
        Add and return a new Node, child of this node.
        """
        return self.forest.add_node(parent_id=self.node_id)

    def load(self, data, title=None):
        head = self
        if title:
            head.add_column(title, color.BOLD)

        def add_list(head, _data):
            for val in _data:
                add_gen(head, val)

        def add_dict(head, _data):
            for key, val in _data.items():
                leaf = head.add_node()
                leaf.add_column(key, color.LIGHTBLUE)
                add_gen(leaf, val)

        def add_gen(head, _data):
            if isinstance(_data, list):
                add_list(head, _data)
            elif isinstance(_data, dict):
                add_dict(head, _data)
            else:
                head.add_column(str(_data))

        add_gen(head, data)

class Forest(object):
    """
    The forest object, offering methods to populate and print the tree.

    Example:

    tree = Forest()
    overall_node = tree.add_node()
    overall_node.add_column("overall")

    node = overall_node.add_node()
    node.add_column("avail")
    node.add_column()
    node.add_column("up", color.GREEN)
    node = node.add_node()
    node.add_column("res#id")
    node.add_column("....")
    node.add_column("up", color.GREEN)
    col = node.add_column("label")
    col.add_text("warn", color.BROWN)
    col.add_text("err", color.RED)

    """
    def __init__(self, separator="  ", widths=None):
        self.data = {
            "data": [],
            "children": []
        }
        self.columns = 1
        self.separator = separator
        self.widths = widths

    def print(self):
        s = forest(self.data, self.columns, separator=self.separator,
                   widths=self.widths)
        try:
            print(s)
        except:
            print(s.encode("utf8", errors="ignore"))

    def __str__(self):
        return forest(self.data, self.columns, separator=self.separator,
                      widths=self.widths)

    def get_node(self, node_id, ref_node=None):
        """
        Return the Node object identified by node_id.
        """
        if ref_node is None:
            ref_node = self.data
        else:
            ref_node = ref_node["children"][node_id[0]]
        if len(node_id) == 1:
            return ref_node
        return self.get_node(node_id[1:], ref_node)

    def add_node(self, parent_id=None):
        """
        Add a node to the forest under the node identified by parent_id.
        """
        if parent_id is None:
            parent_id = []
            parent = self.data
        else:
            parent = self.get_node(parent_id)
        if "children" not in parent:
            parent["children"] = []
        node_id = parent_id + [len(parent["children"])]
        parent["children"].append({})
        return Node(self, node_id)

    def load(self, *args, **kwargs):
        head = self.add_node()
        head.load(*args, **kwargs)

0707010005c8b8000081b40000000000000000000000015ab217540000087b000000660000000000000000000000000000002600000000root/usr/share/opensvc/lib/freezer.py """
Define the Freezer class, instanciated as a Svc lazy attribute,
providing the methods to freeze, thaw a service and to test if
a service is frozen.
"""
import os
from rcGlobalEnv import rcEnv

class Freezer(object):
    """
    The freezer class, instanciated as a Svc lazy attribute.
    Provides methods to freeze, thaw a service and to test if
    the service is frozen.
    """

    def frozen(self, strict=False):
        """
        Return True if the service frozen file flag is present.
        """
        if os.path.exists(self.flag):
            return True
        if not strict and os.path.exists(self.node_flag):
            return True
        return False

    def freeze(self):
        """
        Create the service frozen file flag.
        """
        if os.path.exists(self.flag):
            return
        flag_d = os.path.dirname(self.flag)
        if not os.path.exists(flag_d):
            os.makedirs(flag_d, 0o0755)
        open(self.flag, 'w').close()

    def thaw(self):
        """
        Remove the service frozen file flag.
        """
        if self.flag != self.node_flag and os.path.exists(self.flag):
            os.unlink(self.flag)

    def node_frozen(self):
        """
        Return True if the node frozen file flag is present.
        """
        if os.path.exists(self.node_flag):
            return True
        return False

    def node_freeze(self):
        """
        Create the node frozen file flag.
        """
        if os.path.exists(self.node_flag):
            return
        flag_d = os.path.dirname(self.node_flag)
        if not os.path.exists(flag_d):
            os.makedirs(flag_d, 0o0755)
        open(self.node_flag, 'w').close()

    def node_thaw(self):
        """
        Remove the node frozen file flag.
        """
        if not os.path.exists(self.node_flag):
            return
        os.unlink(self.node_flag)

    def __init__(self, name):
        self.node_flag = os.path.join(rcEnv.paths.pathvar, "node", "frozen")
        if name == "node":
            self.flag = self.node_flag
        else:
            self.flag = os.path.join(rcEnv.paths.pathvar, "services", name, "frozen")
 0707010005c8b9000081b40000000000000000000000015ab2175400002d90000000660000000000000000000000000000002100000000root/usr/share/opensvc/lib/hb.py  """
 Heartbeat parent class
"""
import logging
import time
import socket
import datetime
import struct

import json_delta
import osvcd_shared as shared
from rcGlobalEnv import rcEnv, Storage

class Hb(shared.OsvcThread):
    """
    Heartbeat parent class
    """
    default_hb_period = 5

    def __init__(self, name, role=None):
        shared.OsvcThread.__init__(self)
        self.name = name
        self.id = name + "." + role
        self.log = logging.getLogger(rcEnv.nodename+".osvcd."+self.id)
        self.peers = {}

    def status(self, **kwargs):
        data = shared.OsvcThread.status(self, **kwargs)
        data.peers = {}
        for nodename in self.cluster_nodes:
            if nodename == rcEnv.nodename:
                data.peers[nodename] = {}
                continue
            if "*" in self.peers:
                _data = self.peers["*"]
            else:
                _data = self.peers.get(nodename, Storage({
                    "last": 0,
                    "beating": False,
                    "success": True,
                }))
            data.peers[nodename] = {
                "last": datetime.datetime.utcfromtimestamp(_data.last)\
                                         .strftime('%Y-%m-%dT%H:%M:%SZ'),
                "beating": _data.beating,
            }
        return data

    def set_last(self, nodename="*", success=True):
        if nodename not in self.peers:
            self.peers[nodename] = Storage({
                "last": 0,
                "beating": False,
                "success": True,
            })
        if success:
            self.peers[nodename].last = time.time()
            if not self.peers[nodename].beating:
                self.log.info("node %s hb status stale => beating", nodename)
            self.peers[nodename].beating = True
        self.peers[nodename].success = success

    def get_last(self, nodename="*"):
        if nodename in self.peers:
            return self.peers[nodename]
        return Storage({
            "last": 0,
            "beating": False,
            "success": True,
        })

    def is_beating(self, nodename="*"):
        return self.peers.get(nodename, {"beating": False})["beating"]

    def set_peers_beating(self):
        for nodename in self.peers:
            self.set_beating(nodename)

    def set_beating(self, nodename="*"):
        now = time.time()
        if nodename not in self.peers:
            self.peers[nodename] = Storage({
                "last": 0,
                "beating": False,
                "success": True,
            })
        if now > self.peers[nodename].last + self.timeout:
            beating = False
        else:
            beating = True
        change = False
        if self.peers[nodename].beating != beating:
            change = True
            if beating:
                self.log.info("node %s hb status stale => beating", nodename)
            else:
                self.log.info("node %s hb status beating => stale", nodename)
        self.peers[nodename].beating = beating
        if not beating:
            self.forget_peer_data(nodename, change)

    @staticmethod
    def get_ip_address(ifname):
        try:
            ifname = bytes(ifname, "utf-8")
        except TypeError:
            ifname = str(ifname)
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        import fcntl
        info = fcntl.ioctl(
            s.fileno(),
            0x8915,  # SIOCGIFADDR
            struct.pack('256s', ifname[:15])
        )
        return socket.inet_ntoa(info[20:24])

    def get_message(self, nodename=None):
        begin, num = self.get_oldest_gen(nodename)
        if num == 0:
            # we're alone for now. don't send a full status payload.
            # sent a presence announce payload instead.
            self.log.debug("ping node %s", nodename if nodename else "*")
            message = self.encrypt({
                "kind": "ping",
                "compat": shared.COMPAT_VERSION,
                "gen": self.get_gen(),
                "monitor": self.get_node_monitor(),
                "updated": time.time(), # for hb and relay readers
            }, encode=False)
            return message, len(message) if message else 0
        if begin == 0 or begin > shared.GEN:
            self.log.debug("send full node data to %s", nodename if nodename else "*")
            try:
                shared.CLUSTER_DATA[rcEnv.nodename]["monitor"]["status"]
            except KeyError:
                # no pertinent data to send yet (pre-init)
                self.log.debug("no pertinent data to send yet (pre-init)")
                return None, 0
            if shared.HB_MSG is not None:
                return shared.HB_MSG, shared.HB_MSG_LEN
            with shared.HB_MSG_LOCK:
                shared.HB_MSG = self.encrypt(shared.CLUSTER_DATA[rcEnv.nodename], encode=False)
                if shared.HB_MSG is None:
                    shared.HB_MSG_LEN = 0
                else:
                    shared.HB_MSG_LEN = len(shared.HB_MSG)
                return shared.HB_MSG, shared.HB_MSG_LEN
        else:
            #self.log.info("send gen %d-%d deltas to %s", begin, shared.GEN, nodename if nodename else "*")
            data = {}
            for gen, delta in shared.GEN_DIFF.items():
                if gen <= begin:
                    continue
                data[gen] = delta
            message = self.encrypt({
                "kind": "patch",
                "deltas": data,
                "gen": self.get_gen(),
                "updated": time.time(), # for hb and relay readers
            }, encode=False)
            return message, len(message) if message else 0

    def store_rx_data(self, data, nodename):
        if data is None:
            self.log.info("drop corrupted hb data from %s", nodename)
        current_gen = shared.REMOTE_GEN.get(nodename, 0)
        our_gen_on_peer = data.get("gen", {}).get(rcEnv.nodename, 0)
        kind = data.get("kind", "full")
        change = False
        if kind == "patch":
            if nodename not in shared.CLUSTER_DATA:
                # happens during init. drop the patch, a full will follow
                shared.REMOTE_GEN[nodename] = 0
                shared.LOCAL_GEN[nodename] = our_gen_on_peer
                return
            deltas = data.get("deltas", [])
            gens = sorted([int(gen) for gen in deltas])
            gens = [gen for gen in gens if gen > current_gen]
            if len(gens) == 0:
                #self.log.info("no more recent gen in received deltas")
                if our_gen_on_peer > shared.LOCAL_GEN[nodename]:
                    shared.LOCAL_GEN[nodename] = our_gen_on_peer
                    shared.CLUSTER_DATA[nodename]["gen"][rcEnv.nodename] = our_gen_on_peer
                return
            with shared.CLUSTER_DATA_LOCK:
                for gen in gens:
                    #self.log.debug("merge node %s gen %d (%d diffs)", nodename, gen, len(deltas[str(gen)]))
                    if gen - 1 != current_gen:
                        self.log.warning("unsynchronized node %s dataset. local gen %d, received %d. "
                                         "ask for a full.", nodename, current_gen, gen)
                        shared.REMOTE_GEN[nodename] = 0
                        shared.LOCAL_GEN[nodename] = our_gen_on_peer
                        shared.CLUSTER_DATA[nodename]["gen"] = {
                            nodename: gen,
                            rcEnv.nodename: our_gen_on_peer,
                        }
                        break
                    try:
                        json_delta.patch(shared.CLUSTER_DATA[nodename], deltas[str(gen)])
                        current_gen = gen
                        shared.EVENT_Q.put({
                            "nodename": nodename,
                            "kind": "patch",
                            "data": deltas[str(gen)],
                        })
                        shared.REMOTE_GEN[nodename] = gen
                        shared.LOCAL_GEN[nodename] = our_gen_on_peer
                        shared.CLUSTER_DATA[nodename]["gen"] = {
                            nodename: gen,
                            rcEnv.nodename: our_gen_on_peer,
                        }
                        self.log.debug("patch node %s dataset to gen %d, peer has gen %d of our dataset",
                                       nodename, shared.REMOTE_GEN[nodename],
                                       shared.LOCAL_GEN[nodename])
                        change = True
                    except Exception as exc:
                        self.log.warning("failed to apply node %s dataset gen %d patch: %s. "
                                         "ask for a full: %s", nodename, gen, deltas[str(gen)], exc)
                        shared.REMOTE_GEN[nodename] = 0
                        shared.LOCAL_GEN[nodename] = our_gen_on_peer
                        shared.CLUSTER_DATA[nodename]["gen"] = {
                            nodename: gen,
                            rcEnv.nodename: our_gen_on_peer,
                        }
                        return
        elif kind == "ping":
            with shared.CLUSTER_DATA_LOCK:
                shared.REMOTE_GEN[nodename] = 0
                shared.LOCAL_GEN[nodename] = our_gen_on_peer
                if nodename not in shared.CLUSTER_DATA:
                    shared.CLUSTER_DATA[nodename] = {}
                shared.CLUSTER_DATA[nodename]["gen"] = {
                    nodename: 0,
                    rcEnv.nodename: our_gen_on_peer,
                }
                shared.CLUSTER_DATA[nodename]["monitor"] = data["monitor"]
                self.log.debug("reset node %s dataset gen, peer has gen %d of our dataset",
                              nodename, shared.LOCAL_GEN[nodename])
                change = True
        else:
            data_gen = data.get("gen", {}).get(nodename)
            if data_gen is not None and nodename in shared.LOCAL_GEN and data_gen == shared.LOCAL_GEN[nodename]:
                # already installed
                self.log.debug("already installed %d", data_gen)
                return
            node_status = data.get("monitor", {}).get("status")
            if node_status in ("init", "maintenance", "upgrade") and nodename in shared.CLUSTER_DATA:
                self.duplog("info", "reconduct last known instances status from "
                            "node %(nodename)s in %(node_status)s state",
                            nodename=nodename, node_status=node_status)
                data["services"]["status"] = shared.CLUSTER_DATA[nodename].get("services", {}).get("status", {})
            with shared.CLUSTER_DATA_LOCK:
                shared.CLUSTER_DATA[nodename] = data
                new_gen= data.get("gen", {}).get(nodename, 0)
                shared.LOCAL_GEN[nodename] = our_gen_on_peer
                if new_gen == shared.REMOTE_GEN.get(nodename):
                    return
                shared.REMOTE_GEN[nodename] = new_gen
                shared.CLUSTER_DATA[nodename]["gen"] = {
                    nodename: new_gen,
                    rcEnv.nodename: our_gen_on_peer,
                }
                self.log.debug("install node %s dataset gen %d, peer has gen %d of our dataset",
                              nodename, shared.REMOTE_GEN[nodename],
                              shared.LOCAL_GEN[nodename])
                change = True
        if change:
            shared.wake_monitor("node %s %s dataset gen %d received through %s" % (nodename, kind, shared.REMOTE_GEN[nodename], self.name))

0707010005c8ba000081b40000000000000000000000015ab2175400002d76000000660000000000000000000000000000002600000000root/usr/share/opensvc/lib/hb_disk.py """
Disk Heartbeat
"""
import sys
import os
import mmap
import stat
import errno
import contextlib
import json
import time

import osvcd_shared as shared
import rcExceptions as ex
from rcGlobalEnv import rcEnv, Storage
from comm import Crypt
from hb import Hb
from rcUtilities import bdecode

class HbDisk(Hb, Crypt):
    """
    A class factorizing common methods and properties for the disk
    heartbeat tx and rx child classes.
    """
    # 4MB meta size allows 1024 nodes (with 4k pagesize)
    METASIZE = 4 * 1024 * 1024

    # A 100MB disk can hold 96 nodes
    SLOTSIZE = 1024 * 1024

    MAX_SLOTS = METASIZE // mmap.PAGESIZE
    DEFAULT_DISK_TIMEOUT = 15

    def status(self, **kwargs):
        data = Hb.status(self, **kwargs)
        data.stats = Storage(self.stats)
        data.config = {
            "dev": self.dev,
            "timeout": self.timeout,
        }
        return data

    def configure(self):
        self.dev = None
        self.stats = Storage({
            "beats": 0,
            "bytes": 0,
            "errors": 0,
        })
        self._configure()

    def reconfigure(self):
        self._configure()

    def _configure(self):
        self.peer_config = {}
        if hasattr(self, "node"):
            config = self.node.config
        else:
            config = self.config

        if not hasattr(self, "meta_slot_buff"):
            self.meta_slot_buff = mmap.mmap(-1, 2*mmap.PAGESIZE)
        if not hasattr(self, "slot_buff"):
            self.slot_buff = mmap.mmap(-1, self.SLOTSIZE)

        # timeout
        if self.config.has_option(self.name, "timeout@"+rcEnv.nodename):
            self.timeout = self.config.getint(self.name, "timeout@"+rcEnv.nodename)
        elif self.config.has_option(self.name, "timeout"):
            self.timeout = self.config.getint(self.name, "timeout")
        else:
            self.timeout = self.DEFAULT_DISK_TIMEOUT

        try:
            new_dev = self.config.get(self.name, "dev")
        except Exception:
            raise ex.excAbortAction("no %s.dev is not set in node.conf" % self.name)

        if not os.path.exists(new_dev):
            raise ex.excAbortAction("%s does not exist" % new_dev)

        new_dev = os.path.realpath(new_dev)
        new_flags = os.O_RDWR
        statinfo = os.stat(new_dev)
        if rcEnv.sysname == "Linux":
            if stat.S_ISBLK(statinfo.st_mode):
                self.log.info("using directio")
                new_flags |= os.O_DIRECT | os.O_SYNC | os.O_DSYNC
            else:
                raise ex.excAbortAction("%s must be a block device" % new_dev)
        else:
            if not stat.S_ISCHR(statinfo.st_mode):
                raise ex.excAbortAction("%s must be a char device" % new_dev)

        if new_dev != self.dev:
            self.dev = new_dev
            self.flags = new_flags
            self.peer_config = {}
            self.log.info("set dev=%s", self.dev)

        with self.hb_fo() as fo:
            self.load_peer_config(fo=fo)

    @contextlib.contextmanager
    def hb_fo(self):
        try:
            fd = os.open(self.dev, self.flags)
            fo = os.fdopen(fd, 'rb+')
        except OSError as exc:
            if exc.errno == errno.EINVAL:
                raise ex.excAbortAction("%s directio is not supported" % self.dev)
            else:
                raise ex.excAbortAction("error opening %s: %s" % (self.dev, str(exc)))
        except Exception as exc:
            raise ex.excAbortAction("error opening %s: %s" % (self.dev, str(exc)))
        try:
            yield fo
        except Exception as exc:
            self.log.error("%s: %s", self.dev, exc)
        finally:
            # closing fo also closes fd
            try:
                os.fsync(fd)
            except OSError as exc:
                self.duplog("error", "%(exc)s", exc=str(exc), nodename="")
            fo.close()

    @staticmethod
    def meta_slot_offset(slot):
        return slot * mmap.PAGESIZE

    def meta_read_slot(self, slot, fo=None):
        offset = self.meta_slot_offset(slot)
        fo.seek(offset, os.SEEK_SET)
        fo.readinto(self.meta_slot_buff)
        try:
            return bdecode(self.meta_slot_buff[:mmap.PAGESIZE])
        except Exception as exc:
            return None

    def meta_write_slot(self, slot, data, fo=None):
        if len(data) > mmap.PAGESIZE:
            self.log.error("attempt to write too long data in meta slot %d", slot)
            raise ex.excAbortAction()
        self.meta_slot_buff.seek(0)
        self.meta_slot_buff.write(data)
        offset = self.meta_slot_offset(slot)
        fo.seek(offset, os.SEEK_SET)
        fo.write(self.meta_slot_buff)
        fo.flush()

    def slot_offset(self, slot):
        return self.METASIZE + slot * self.SLOTSIZE

    def read_slot(self, slot, fo=None):
        offset = self.slot_offset(slot)
        fo.seek(offset, os.SEEK_SET)
        fo.readinto(self.slot_buff)
        data = bdecode(self.slot_buff[:])
        end = data.index("\0")
        return data[:end]

    def write_slot(self, slot, data, fo=None):
        if len(data) > self.SLOTSIZE:
            self.log.error("attempt to write too long data in slot %d", slot)
            raise ex.excAbortAction()
        self.slot_buff.seek(0)
        self.slot_buff.write(data)
        offset = self.slot_offset(slot)
        fo.seek(offset, os.SEEK_SET)
        fo.write(self.slot_buff)
        fo.flush()

    def load_peer_config(self, fo=None, verbose=True):
        for nodename in self.cluster_nodes:
            if nodename not in self.peer_config:
                self.peer_config[nodename] = Storage({
                    "slot": -1,
                })
        for slot in range(self.MAX_SLOTS):
            buff = self.meta_read_slot(slot, fo=fo)
            if buff is None or buff[0] == "\0":
                return
            try:
                nodename = buff[:buff.index("\0")]
            except IndexError:
                continue
            if nodename not in self.peer_config:
                continue
            if self.peer_config[nodename].slot >= 0:
                if verbose:
                    self.log.warning("duplicate slot %d for node %s (first %d)",
                                     slot, nodename,
                                     self.peer_config[nodename].slot)
                continue
            if verbose:
                self.log.info("detect slot %d for node %s", slot, nodename)
            self.peer_config[nodename]["slot"] = slot

    def allocate_slot(self):
        for slot in range(self.MAX_SLOTS):
            with self.hb_fo() as fo:
                buff = self.meta_read_slot(slot, fo=fo)
                if buff is None or buff[0] != "\0":
                    continue
                self.peer_config[rcEnv.nodename].slot = slot
                try:
                    nodename = bytes(rcEnv.nodename, "utf-8")
                except TypeError:
                    nodename = rcEnv.nodename
                self.meta_write_slot(slot, nodename, fo=fo)
                self.log.info("allocated slot %d", slot)
            break


class HbDiskTx(HbDisk):
    """
    The disk heartbeat tx class.
    """
    def __init__(self, name):
        HbDisk.__init__(self, name, role="tx")

    def _configure(self):
        HbDisk._configure(self)

    def run(self):
        try:
            self.configure()
            if self.peer_config[rcEnv.nodename].slot < 0:
                self.allocate_slot()
        except ex.excAbortAction as exc:
            self.log.error(exc)
            self.stop()
            sys.exit(1)

        try:
            while True:
                self.do()
                if self.stopped():
                    sys.exit(0)
                with shared.HB_TX_TICKER:
                    shared.HB_TX_TICKER.wait(self.default_hb_period)
        except Exception as exc:
            self.log.exception(exc)

    def status(self, **kwargs):
        data = HbDisk.status(self, **kwargs)
        data["config"] = {}
        return data

    def do(self):
        with self.hb_fo() as fo:
            self._do(fo)

    def _do(self, fo):
        self.reload_config()
        if rcEnv.nodename not in self.peer_config:
            return
        slot = self.peer_config[rcEnv.nodename].slot
        if slot < 0:
            return
        message, message_bytes = self.get_message()
        if message is None:
            return

        data = (json.dumps({
            "msg": message,
            "updated": time.time(),
        })+'\0').encode()
        try:
            self.write_slot(slot, data, fo=fo)
            self.set_last()
            self.stats.beats += 1
            self.stats.bytes += message_bytes
            #self.log.info("written to %s slot %s", self.dev, slot)
        except Exception as exc:
            self.stats.errors += 1
            if self.get_last().success:
                self.log.error("write to %s slot %d error: %s", self.dev,
                               self.peer_config[rcEnv.nodename]["slot"], exc)
            self.set_last(success=False)
        finally:
            self.set_beating()

class HbDiskRx(HbDisk):
    """
    The disk heartbeat rx class.
    """
    def __init__(self, name):
        HbDisk.__init__(self, name, role="rx")
        self.last_updated = {}

    def run(self):
        try:
            self.configure()
        except ex.excAbortAction as exc:
            self.log.error(exc)
            self.stop()
            sys.exit(1)

        while True:
            self.do()
            if self.stopped():
                sys.exit(0)
            with shared.HB_TX_TICKER:
                shared.HB_TX_TICKER.wait(self.default_hb_period)

    def do(self):
        with self.hb_fo() as fo:
            self._do(fo)

    def _do(self, fo):
        self.reload_config()
        for nodename, data in self.peer_config.items():
            if nodename == rcEnv.nodename:
                continue
            if data.slot < 0:
                continue
            try:
                slot_data = json.loads(self.read_slot(data.slot, fo=fo))
                _nodename, _data = self.decrypt(slot_data["msg"])
                if _nodename is None:
                    # invalid crypt
                    #self.log.warning("can't decrypt data in node %s slot",
                    #                 nodename)
                    continue
                if _nodename != nodename:
                    self.log.warning("node %s has written its data in node %s "
                                     "reserved slot", _nodename, nodename)
                    nodename = _nodename
                updated = slot_data["updated"]
                last_updated = self.last_updated.get(nodename)
                if last_updated is not None and last_updated == updated:
                    # remote tx has not rewritten its slot
                    #self.log.info("node %s has not updated its slot", nodename)
                    continue
                self.last_updated[nodename] = updated
                self.store_rx_data(_data, nodename)
                self.stats.beats += 1
                self.stats.bytes += len(slot_data)
                self.set_last(nodename)
            except Exception as exc:
                self.stats.errors += 1
                if self.get_last(nodename).success:
                    self.log.error("read from %s slot %d (%s) error: %s", self.dev,
                                   data.slot, nodename, str(exc))
                self.set_last(nodename, success=False)
            finally:
                self.set_beating(nodename)



  0707010005c8bb000081b40000000000000000000000015ab2175400001ab7000000660000000000000000000000000000002700000000root/usr/share/opensvc/lib/hb_mcast.py    """
Multicast Heartbeat
"""
import sys
import socket
import threading
import struct

import rcExceptions as ex
import osvcd_shared as shared
from rcGlobalEnv import rcEnv, Storage
from comm import Crypt
from hb import Hb

class HbMcast(Hb, Crypt):
    """
    A class factorizing common methods and properties for the multicast
    heartbeat tx and rx child classes.
    """
    DEFAULT_MCAST_PORT = 10000
    DEFAULT_MCAST_ADDR = "224.3.29.71"
    DEFAULT_MCAST_TIMEOUT = 15

    def status(self, **kwargs):
        data = Hb.status(self, **kwargs)
        data.stats = Storage(self.stats)
        data.config = {
            "addr": self.addr,
            "port": self.port,
            "intf": self.intf,
            "src_addr": self.src_addr,
            "timeout": self.timeout,
        }
        return data

    def configure(self):
        self.stats = Storage({
            "beats": 0,
            "bytes": 0,
            "errors": 0,
        })
        self._configure()

    def reconfigure(self):
        self._configure()

    def _configure(self):
        try:
            self.port = self.config.getint(self.name, "port")
        except Exception:
            self.port = self.DEFAULT_MCAST_PORT
        try:
            self.addr = self.config.get(self.name, "addr")
        except Exception:
            self.addr = self.DEFAULT_MCAST_ADDR
        try:
            self.timeout = self.config.getint(self.name, "timeout")
        except Exception:
            self.timeout = self.DEFAULT_MCAST_TIMEOUT
        group = socket.inet_aton(self.addr)
        try:
            self.intf = self.config.get(self.name, "intf")
            self.src_addr = self.get_ip_address(self.intf)
            self.mreq = group + socket.inet_aton(self.src_addr)
        except Exception:
            self.intf = "any"
            self.src_addr = "0.0.0.0"
            self.mreq = struct.pack("4sl", group, socket.INADDR_ANY)
        self.max_handlers = len(self.cluster_nodes) * 4

    def set_if(self):
        if self.intf == "any":
            return
        try:
            intf_b = bytes(self.intf, "utf-8")
        except TypeError:
            intf_b = str(self.intf)
        try:
            # Linux only
            self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BINDTODEVICE, intf_b)
        except AttributeError:
            pass
        self.sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, self.mreq)

class HbMcastTx(HbMcast):
    """
    The multicast heartbeat tx class.
    """
    def __init__(self, name):
        HbMcast.__init__(self, name, role="tx")

    def run(self):
        try:
            self.configure()
        except ex.excAbortAction:
            return

        try:
            addrinfo = socket.getaddrinfo(self.addr, None)[0]
            ttl = struct.pack('b', 32)
            self.addr = addrinfo[4][0]
            self.group = (self.addr, self.port)
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self.set_if()
            self.sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl)
            self.sock.settimeout(2)
        except socket.error as exc:
            self.log.error("init error: %s", str(exc))
            return

        try:
            while True:
                self.do()
                if self.stopped():
                    self.sock.close()
                    sys.exit(0)
                with shared.HB_TX_TICKER:
                    shared.HB_TX_TICKER.wait(self.default_hb_period)
        except Exception as exc:
            self.log.exception(exc)

    def do(self):
        self.reload_config()
        message, message_bytes = self.get_message()
        if message is None:
            return

        #self.log.info("sending to %s:%s", self.addr, self.port)
        try:
            sent = self.sock.sendto((message+"\0").encode(), self.group)
            self.set_last()
            self.stats.beats += 1
            self.stats.bytes += message_bytes
        except socket.timeout as exc:
            self.stats.errors += 1
            if self.get_last().success:
                self.log.warning("send timeout")
            self.set_last(success=False)
        except socket.error as exc:
            self.stats.errors += 1
            if self.get_last().success:
                self.log.warning("send error: %s", exc)
            self.set_last(success=False)
        finally:
            self.set_beating()


#
class HbMcastRx(HbMcast):
    """
    The multicast heartbeat rx class.
    """
    def __init__(self, name):
        HbMcast.__init__(self, name, role="rx")

    def run(self):
        try:
            self.configure()
        except ex.excAbortAction:
            return
        try:
            addrinfo = socket.getaddrinfo(self.addr, None)[0]
            self.addr = addrinfo[4][0]
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self.set_if()
            self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, self.mreq)
            self.sock.bind(('', self.port))
            self.sock.settimeout(2)
        except socket.error as exc:
            self.log.error("init error: %s", str(exc))
            return

        self.log.info("listening on %s:%s", self.addr, self.port)

        while True:
            self.do()
            if self.stopped():
                self.join_threads()
                self.sock.close()
                sys.exit(0)

    def do(self):
        self.reload_config()
        self.janitor_threads()

        try:
            data, addr = self.sock.recvfrom(shared.MAX_MSG_SIZE)
            self.stats.beats += 1
            self.stats.bytes += len(data)
        except socket.timeout:
            self.set_peers_beating()
            return
        if len(self.threads) >= self.max_handlers:
            self.log.warning("drop message received from %s: too many running handlers (%d)",
                             addr, self.max_handlers)
            return
        thr = threading.Thread(target=self.handle_client, args=(data, addr))
        thr.start()
        self.threads.append(thr)

    def handle_client(self, message, addr):
        nodename, data = self.decrypt(message, sender_id=addr[0])
        if nodename is None or nodename == rcEnv.nodename:
            # ignore hb data we sent ourself
            return
        elif nodename not in self.cluster_nodes:
            # decrypt passed, trust it is a new node
            self.add_cluster_node(nodename)
        if data is None:
            self.stats.errors += 1
            self.set_beating(nodename)
            return
        self.store_rx_data(data, nodename)
        self.set_last(nodename)
        self.set_beating(nodename)
        self.set_peers_beating()



 0707010005c8bc000081b40000000000000000000000015ab2175400001b68000000660000000000000000000000000000002700000000root/usr/share/opensvc/lib/hb_relay.py    """
Relay Heartbeat
"""
import sys
import os
import errno
import contextlib

import osvcd_shared as shared
import rcExceptions as ex
from rcGlobalEnv import rcEnv, Storage
from comm import Crypt
from hb import Hb

class HbRelay(Hb, Crypt):
    """
    A class factorizing common methods and properties for the relay
    heartbeat tx and rx child classes.
    """
    DEFAULT_TIMEOUT = 15

    def status(self, **kwargs):
        data = Hb.status(self, **kwargs)
        data.stats = Storage(self.stats)
        data.config = {
            "timeout": self.timeout,
        }
        if hasattr(self, "relay"):
            data.config["relay"] = self.relay
        return data

    def configure(self):
        self.stats = Storage({
            "beats": 0,
            "bytes": 0,
            "errors": 0,
        })
        self._configure()

    def reconfigure(self):
        self._configure()

    def _configure(self):
        self.peer_config = {}
        if hasattr(self, "node"):
            config = self.node.config
        else:
            config = self.config

        # timeout
        if self.config.has_option(self.name, "timeout@"+rcEnv.nodename):
            self.timeout = self.config.getint(self.name, "timeout@"+rcEnv.nodename)
        elif self.config.has_option(self.name, "timeout"):
            self.timeout = self.config.getint(self.name, "timeout")
        else:
            self.timeout = self.DEFAULT_TIMEOUT

        try:
            self.relay = self.config.get(self.name, "relay")
        except Exception:
            raise ex.excAbortAction("no %s.relay is not set in node.conf" % self.name)
        try:
            self.secret = self.config.get(self.name, "secret")
        except Exception:
            raise ex.excAbortAction("no %s.secret is not set in node.conf" % self.name)

class HbRelayTx(HbRelay):
    """
    The relay heartbeat tx class.
    """
    def __init__(self, name):
        HbRelay.__init__(self, name, role="tx")

    def _configure(self):
        HbRelay._configure(self)

    def run(self):
        self.flags = os.O_RDWR
        try:
            self.configure()
        except ex.excAbortAction as exc:
            self.log.error(exc)
            self.stop()
            sys.exit(1)

        try:
            while True:
                self.do()
                if self.stopped():
                    sys.exit(0)
                with shared.HB_TX_TICKER:
                    shared.HB_TX_TICKER.wait(self.default_hb_period)
        except Exception as exc:
            self.log.exception(exc)

    def status(self, **kwargs):
        data = HbRelay.status(self, **kwargs)
        data["config"] = {}
        return data

    def do(self):
        self.reload_config()
        message, message_bytes = self.get_message()
        if message is None:
            return

        try:
            self.send(message)
            self.set_last()
            self.stats.beats += 1
            self.stats.bytes += message_bytes
            #self.log.info("written to %s slot %s", self.dev, slot)
        except Exception as exc:
            self.stats.errors += 1
            if self.get_last().success:
                self.log.error("send to relay error: %s", exc)
            self.set_last(success=False)
        finally:
            self.set_beating()

    def send(self, message):
        request = {
            "action": "relay_tx",
            "options": {
                "msg": message,
            },
        }
        resp = self.daemon_send(request, cluster_name="join", nodename=self.relay, secret=self.secret)
        if resp is None:
            raise ex.excError("not responding")
        if resp.get("status", 1) != 0:
            raise ex.excError("return status not 0")



class HbRelayRx(HbRelay):
    """
    The relay heartbeat rx class.
    """
    def __init__(self, name):
        HbRelay.__init__(self, name, role="rx")
        self.last_updated = {}

    def run(self):
        self.flags = os.O_RDWR
        try:
            self.configure()
        except ex.excAbortAction as exc:
            self.log.error(exc)
            self.stop()
            sys.exit(1)
        self.log.info("receive from relay %s", self.relay)

        while True:
            self.do()
            if self.stopped():
                sys.exit(0)
            with shared.HB_TX_TICKER:
                shared.HB_TX_TICKER.wait(self.default_hb_period)

    def do(self):
        self.reload_config()
        for nodename in self.cluster_nodes:
            if nodename == rcEnv.nodename:
                continue
            try:
                updated, slot_data = self.receive(nodename)
                _nodename, _data = self.decrypt(slot_data, sender_id=self.relay)
                if _nodename is None:
                    # invalid crypt
                    #self.log.warning("can't decrypt data in node %s slot",
                    #                 nodename)
                    continue
                if _nodename != nodename:
                    self.log.warning("node %s has written its data in node %s "
                                     "reserved slot", _nodename, nodename)
                    nodename = _nodename
                last_updated = self.last_updated.get(nodename)
                if last_updated is not None and last_updated == updated:
                    # remote tx has not rewritten its slot
                    #self.log.info("node %s has not updated its slot", nodename)
                    continue
                self.last_updated[nodename] = updated
                self.store_rx_data(_data, nodename)
                self.stats.beats += 1
                self.set_last(nodename)
            except Exception as exc:
                self.stats.errors += 1
                if self.get_last(nodename).success:
                    self.log.error("read from relay %s slot %s error: %s", self.relay,
                                   nodename, str(exc))
                self.set_last(nodename, success=False)
            finally:
                self.set_beating(nodename)

    def receive(self, nodename):
        request = {
            "action": "relay_rx",
            "options": {
                "slot": nodename,
            },
        }
        resp = self.daemon_send(request, cluster_name="join", nodename=self.relay, secret=self.secret)
        if resp is None:
            raise ex.excError("no response reading relay slot %s" % nodename)
        if resp.get("status", 1) != 0:
            raise ex.excError("return status not 0 reading relay slot %s" % nodename)
        if resp.get("data") is None:
            raise ex.excError("no data in response reading relay slot %s" % nodename)
        if resp.get("updated") is None:
            raise ex.excError("no 'updated' key in response reading relay slot %s" % nodename)
        try:
            # python3
            return resp.get("updated"), bytes(resp["data"], "ascii")
        except TypeError:
            return resp.get("updated"), resp["data"]

0707010005c8bd000081b40000000000000000000000015ab2175400001df4000000660000000000000000000000000000002700000000root/usr/share/opensvc/lib/hb_ucast.py    """
Unicast Heartbeat module
"""
import sys
import socket
import threading

import rcExceptions as ex
import osvcd_shared as shared
from rcGlobalEnv import rcEnv, Storage
from comm import Crypt
from hb import Hb

class HbUcast(Hb, Crypt):
    """
    A class factorizing common methods and properties for the unicast
    heartbeat tx and rx child classes.
    """
    DEFAULT_UCAST_PORT = 10000
    DEFAULT_UCAST_TIMEOUT = 15

    def status(self, **kwargs):
        data = Hb.status(self, **kwargs)
        data.stats = Storage(self.stats)
        data.config = {
            "addr": self.peer_config[rcEnv.nodename].addr,
            "port": self.peer_config[rcEnv.nodename].port,
            "timeout": self.timeout,
        }
        return data

    def configure(self):
        self.stats = Storage({
            "beats": 0,
            "bytes": 0,
            "errors": 0,
        })
        self._configure()

    def reconfigure(self):
        self._configure()

    def _configure(self):
        self.peer_config = {}
        if hasattr(self, "node"):
            config = self.node.config
        else:
            config = self.config
        try:
            default_port = config.getint(self.name, "port")
        except Exception:
            default_port = self.DEFAULT_UCAST_PORT + 0

        for nodename in self.cluster_nodes:
            if nodename not in self.peer_config:
                if nodename == rcEnv.nodename:
                    default_addr = "0.0.0.0"
                else:
                    default_addr = nodename
                self.peer_config[nodename] = Storage({
                    "addr": default_addr,
                    "port": default_port,
                })
            if config.has_option(self.name, "addr@"+nodename):
                self.peer_config[nodename].addr = \
                    config.get(self.name, "addr@"+nodename)
            if config.has_option(self.name, "port@"+nodename):
                self.peer_config[nodename].port = \
                    config.getint(self.name, "port@"+nodename)

        # timeout
        if self.config.has_option(self.name, "timeout@"+rcEnv.nodename):
            self.timeout = \
                self.config.getint(self.name, "timeout@"+rcEnv.nodename)
        elif self.config.has_option(self.name, "timeout"):
            self.timeout = self.config.getint(self.name, "timeout")
        else:
            self.timeout = self.DEFAULT_UCAST_TIMEOUT
        self.max_handlers = len(self.cluster_nodes) * 4

class HbUcastTx(HbUcast):
    """
    The unicast heartbeat tx class.
    """
    def __init__(self, name):
        HbUcast.__init__(self, name, role="tx")

    def run(self):
        try:
            self.configure()
        except ex.excAbortAction:
            return

        try:
            while True:
                self.do()
                if self.stopped():
                    sys.exit(0)
                with shared.HB_TX_TICKER:
                    shared.HB_TX_TICKER.wait(self.default_hb_period)
        except Exception as exc:
            self.log.exception(exc)

    def status(self, **kwargs):
        data = HbUcast.status(self, **kwargs)
        data["config"] = {}
        return data

    def do(self):
        self.reload_config()
        message, message_bytes = self.get_message()
        if message is None:
            return

        for nodename, config in self.peer_config.items():
            if nodename == rcEnv.nodename:
                continue
            self._do(message, message_bytes, nodename, config)

    def _do(self, message, message_bytes, nodename, config):
        try:
            #self.log.info("sending to %s:%s", config.addr, config.port)
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(1)
            sock.bind((self.peer_config[rcEnv.nodename].addr, 0))
            sock.connect((config.addr, config.port))
            sock.sendall((message+"\0").encode())
            self.set_last(nodename)
            self.stats.beats += 1
            self.stats.bytes += message_bytes
        except socket.timeout as exc:
            self.stats.errors += 1
            if self.get_last(nodename).success:
                self.log.warning("send to %s (%s:%d) timeout", nodename,
                                 config.addr, config.port)
            self.set_last(nodename, success=False)
        except socket.error as exc:
            self.stats.errors += 1
            if self.get_last(nodename).success:
                self.log.warning("send to %s (%s:%d) error: %s", nodename,
                               config.addr, config.port, str(exc))
            self.set_last(nodename, success=False)
        finally:
            self.set_beating(nodename)
            sock.close()

class HbUcastRx(HbUcast):
    """
    The unicast heartbeat rx class.
    """
    def __init__(self, name):
        HbUcast.__init__(self, name, role="rx")

    def run(self):
        try:
            self.configure()
        except ex.excAbortAction:
            return
        try:
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.sock.bind((self.peer_config[rcEnv.nodename].addr,
                            self.peer_config[rcEnv.nodename].port))
            self.sock.listen(5)
            self.sock.settimeout(2)
        except socket.error as exc:
            self.log.error("init error: %s", str(exc))
            return

        self.log.info("listening on %s:%s",
                      self.peer_config[rcEnv.nodename].addr,
                      self.peer_config[rcEnv.nodename].port)

        while True:
            self.do()
            if self.stopped():
                self.join_threads()
                self.sock.close()
                sys.exit(0)

    def do(self):
        self.reload_config()
        self.janitor_threads()

        try:
            conn, addr = self.sock.accept()
        except socket.timeout:
            return
        finally:
            self.set_peers_beating()
        if len(self.threads) >= self.max_handlers:
            self.log.warning("drop message received from %s: too many running handlers (%d)",
                             addr, self.max_handlers)
            return
        thr = threading.Thread(target=self.handle_client, args=(conn, addr))
        thr.start()
        self.threads.append(thr)

    def handle_client(self, conn, addr):
        try:
            self._handle_client(conn, addr)
            self.stats.beats += 1
        finally:
            conn.close()

    def _handle_client(self, conn, addr):
        chunks = []
        buff_size = 4096
        while True:
            chunk = conn.recv(buff_size)
            self.stats.bytes += len(chunk)
            if chunk:
                chunks.append(chunk)
            if not chunk or chunk.endswith(b"\x00"):
                break
        if sys.version_info[0] >= 3:
            data = b"".join(chunks)
        else:
            data = "".join(chunks)
        del chunks

        nodename, data = self.decrypt(data, sender_id=addr[0])
        if nodename is None or nodename == rcEnv.nodename:
            # ignore hb data we sent ourself
            return
        elif nodename not in self.cluster_nodes:
            # decrypt passed, trust it is a new node
            self.add_cluster_node(nodename)
        if data is None:
            self.stats.errors += 1
            self.set_beating(nodename)
            return
        self.store_rx_data(data, nodename)
        self.set_last(nodename)
        self.set_beating(nodename)



0707010005c8be000081b40000000000000000000000015ab217540000011d000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/hostidAIX.py   from subprocess import *

def hostid():
    cmd = ['uname', '-u']
    p = Popen(cmd, stderr=None, stdout=PIPE, close_fds=True)
    buff = p.communicate()
    if p.returncode != 0:
        return '1'
    sn = buff[0].split()[0]
    return str(hex(abs(sn.__hash__()))).replace('0x', '')
   0707010005c8bf000081b40000000000000000000000015ab2175400000042000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/hostidDarwin.py    from uuid import getnode

def hostid():
    return str(getnode())
  0707010005c8c0000081b40000000000000000000000015ab217540000017d000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/hostidHP-UX.py from rcUtilities import which
from subprocess import *

def hostid():
    if which('getconf') is None:
        return '1'
    cmd = ['getconf', 'MACHINE_SERIAL']
    p = Popen(cmd, stderr=None, stdout=PIPE, close_fds=True)
    buff = p.communicate()
    sn = buff[0].split()[0]
    if p.returncode != 0:
        return '1'
    return str(hex(abs(sn.__hash__()))).replace('0x', '')
   0707010005c8c1000081b40000000000000000000000015ab2175400000042000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/hostidLinux.py from uuid import getnode

def hostid():
    return str(getnode())
  0707010005c8c2000081b40000000000000000000000015ab2175400000042000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/hostidOSF1.py  from uuid import getnode

def hostid():
    return str(getnode())
  0707010005c8c3000081b40000000000000000000000015ab2175400000042000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/hostidSunOS.py from uuid import getnode

def hostid():
    return str(getnode())
  0707010005c8c4000081b40000000000000000000000015ab2175400000042000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/hostidWindows.py   from uuid import getnode

def hostid():
    return str(getnode())
  0707010005c8c5000081b40000000000000000000000015ab2175400013820000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/ipaddress.py   # Copyright 2007 Google Inc.
#  Licensed to PSF under a Contributor Agreement.

"""A fast, lightweight IPv4/IPv6 manipulation library in Python.

This library is used to create/poke/manipulate IPv4 and IPv6 addresses
and networks.

"""

from __future__ import unicode_literals


import itertools
import struct

__version__ = '1.0.16'

# Compatibility functions
_compat_int_types = (int,)
try:
    _compat_int_types = (int, long)
except NameError:
    pass
try:
    _compat_str = unicode
except NameError:
    _compat_str = str
    assert bytes != str
if b'\0'[0] == 0:  # Python 3 semantics
    def _compat_bytes_to_byte_vals(byt):
        return byt
else:
    def _compat_bytes_to_byte_vals(byt):
        return [struct.unpack(b'!B', b)[0] for b in byt]
try:
    _compat_int_from_byte_vals = int.from_bytes
except AttributeError:
    def _compat_int_from_byte_vals(bytvals, endianess):
        assert endianess == 'big'
        res = 0
        for bv in bytvals:
            assert isinstance(bv, _compat_int_types)
            res = (res << 8) + bv
        return res


def _compat_to_bytes(intval, length, endianess):
    assert isinstance(intval, _compat_int_types)
    assert endianess == 'big'
    if length == 4:
        if intval < 0 or intval >= 2 ** 32:
            raise struct.error("integer out of range for 'I' format code")
        return struct.pack(b'!I', intval)
    elif length == 16:
        if intval < 0 or intval >= 2 ** 128:
            raise struct.error("integer out of range for 'QQ' format code")
        return struct.pack(b'!QQ', intval >> 64, intval & 0xffffffffffffffff)
    else:
        raise NotImplementedError()
if hasattr(int, 'bit_length'):
    # Not int.bit_length , since that won't work in 2.7 where long exists
    def _compat_bit_length(i):
        return i.bit_length()
else:
    def _compat_bit_length(i):
        for res in itertools.count():
            if i >> res == 0:
                return res


def _compat_range(start, end, step=1):
    assert step > 0
    i = start
    while i < end:
        yield i
        i += step


class _TotalOrderingMixin(object):
    __slots__ = ()

    # Helper that derives the other comparison operations from
    # __lt__ and __eq__
    # We avoid functools.total_ordering because it doesn't handle
    # NotImplemented correctly yet (http://bugs.python.org/issue10042)
    def __eq__(self, other):
        raise NotImplementedError

    def __ne__(self, other):
        equal = self.__eq__(other)
        if equal is NotImplemented:
            return NotImplemented
        return not equal

    def __lt__(self, other):
        raise NotImplementedError

    def __le__(self, other):
        less = self.__lt__(other)
        if less is NotImplemented or not less:
            return self.__eq__(other)
        return less

    def __gt__(self, other):
        less = self.__lt__(other)
        if less is NotImplemented:
            return NotImplemented
        equal = self.__eq__(other)
        if equal is NotImplemented:
            return NotImplemented
        return not (less or equal)

    def __ge__(self, other):
        less = self.__lt__(other)
        if less is NotImplemented:
            return NotImplemented
        return not less


IPV4LENGTH = 32
IPV6LENGTH = 128


class AddressValueError(ValueError):
    """A Value Error related to the address."""


class NetmaskValueError(ValueError):
    """A Value Error related to the netmask."""


def ip_address(address):
    """Take an IP string/int and return an object of the correct type.

    Args:
        address: A string or integer, the IP address.  Either IPv4 or
          IPv6 addresses may be supplied; integers less than 2**32 will
          be considered to be IPv4 by default.

    Returns:
        An IPv4Address or IPv6Address object.

    Raises:
        ValueError: if the *address* passed isn't either a v4 or a v6
          address

    """
    try:
        return IPv4Address(address)
    except (AddressValueError, NetmaskValueError):
        pass

    try:
        return IPv6Address(address)
    except (AddressValueError, NetmaskValueError):
        pass

    if isinstance(address, bytes):
        raise AddressValueError(
            '%r does not appear to be an IPv4 or IPv6 address. '
            'Did you pass in a bytes (str in Python 2) instead of'
            ' a unicode object?' % address)

    raise ValueError('%r does not appear to be an IPv4 or IPv6 address' %
                     address)


def ip_network(address, strict=True):
    """Take an IP string/int and return an object of the correct type.

    Args:
        address: A string or integer, the IP network.  Either IPv4 or
          IPv6 networks may be supplied; integers less than 2**32 will
          be considered to be IPv4 by default.

    Returns:
        An IPv4Network or IPv6Network object.

    Raises:
        ValueError: if the string passed isn't either a v4 or a v6
          address. Or if the network has host bits set.

    """
    try:
        return IPv4Network(address, strict)
    except (AddressValueError, NetmaskValueError):
        pass

    try:
        return IPv6Network(address, strict)
    except (AddressValueError, NetmaskValueError):
        pass

    if isinstance(address, bytes):
        raise AddressValueError(
            '%r does not appear to be an IPv4 or IPv6 network. '
            'Did you pass in a bytes (str in Python 2) instead of'
            ' a unicode object?' % address)

    raise ValueError('%r does not appear to be an IPv4 or IPv6 network' %
                     address)


def ip_interface(address):
    """Take an IP string/int and return an object of the correct type.

    Args:
        address: A string or integer, the IP address.  Either IPv4 or
          IPv6 addresses may be supplied; integers less than 2**32 will
          be considered to be IPv4 by default.

    Returns:
        An IPv4Interface or IPv6Interface object.

    Raises:
        ValueError: if the string passed isn't either a v4 or a v6
          address.

    Notes:
        The IPv?Interface classes describe an Address on a particular
        Network, so they're basically a combination of both the Address
        and Network classes.

    """
    try:
        return IPv4Interface(address)
    except (AddressValueError, NetmaskValueError):
        pass

    try:
        return IPv6Interface(address)
    except (AddressValueError, NetmaskValueError):
        pass

    raise ValueError('%r does not appear to be an IPv4 or IPv6 interface' %
                     address)


def v4_int_to_packed(address):
    """Represent an address as 4 packed bytes in network (big-endian) order.

    Args:
        address: An integer representation of an IPv4 IP address.

    Returns:
        The integer address packed as 4 bytes in network (big-endian) order.

    Raises:
        ValueError: If the integer is negative or too large to be an
          IPv4 IP address.

    """
    try:
        return _compat_to_bytes(address, 4, 'big')
    except (struct.error, OverflowError):
        raise ValueError("Address negative or too large for IPv4")


def v6_int_to_packed(address):
    """Represent an address as 16 packed bytes in network (big-endian) order.

    Args:
        address: An integer representation of an IPv6 IP address.

    Returns:
        The integer address packed as 16 bytes in network (big-endian) order.

    """
    try:
        return _compat_to_bytes(address, 16, 'big')
    except (struct.error, OverflowError):
        raise ValueError("Address negative or too large for IPv6")


def _split_optional_netmask(address):
    """Helper to split the netmask and raise AddressValueError if needed"""
    addr = _compat_str(address).split('/')
    if len(addr) > 2:
        raise AddressValueError("Only one '/' permitted in %r" % address)
    return addr


def _find_address_range(addresses):
    """Find a sequence of sorted deduplicated IPv#Address.

    Args:
        addresses: a list of IPv#Address objects.

    Yields:
        A tuple containing the first and last IP addresses in the sequence.

    """
    it = iter(addresses)
    first = last = next(it)
    for ip in it:
        if ip._ip != last._ip + 1:
            yield first, last
            first = ip
        last = ip
    yield first, last


def _count_righthand_zero_bits(number, bits):
    """Count the number of zero bits on the right hand side.

    Args:
        number: an integer.
        bits: maximum number of bits to count.

    Returns:
        The number of zero bits on the right hand side of the number.

    """
    if number == 0:
        return bits
    return min(bits, _compat_bit_length(~number & (number - 1)))


def summarize_address_range(first, last):
    """Summarize a network range given the first and last IP addresses.

    Example:
        >>> list(summarize_address_range(IPv4Address('192.0.2.0'),
        ...                              IPv4Address('192.0.2.130')))
        ...                                #doctest: +NORMALIZE_WHITESPACE
        [IPv4Network('192.0.2.0/25'), IPv4Network('192.0.2.128/31'),
         IPv4Network('192.0.2.130/32')]

    Args:
        first: the first IPv4Address or IPv6Address in the range.
        last: the last IPv4Address or IPv6Address in the range.

    Returns:
        An iterator of the summarized IPv(4|6) network objects.

    Raise:
        TypeError:
            If the first and last objects are not IP addresses.
            If the first and last objects are not the same version.
        ValueError:
            If the last object is not greater than the first.
            If the version of the first address is not 4 or 6.

    """
    if (not (isinstance(first, _BaseAddress) and
             isinstance(last, _BaseAddress))):
        raise TypeError('first and last must be IP addresses, not networks')
    if first.version != last.version:
        raise TypeError("%s and %s are not of the same version" % (
                        first, last))
    if first > last:
        raise ValueError('last IP address must be greater than first')

    if first.version == 4:
        ip = IPv4Network
    elif first.version == 6:
        ip = IPv6Network
    else:
        raise ValueError('unknown IP version')

    ip_bits = first._max_prefixlen
    first_int = first._ip
    last_int = last._ip
    while first_int <= last_int:
        nbits = min(_count_righthand_zero_bits(first_int, ip_bits),
                    _compat_bit_length(last_int - first_int + 1) - 1)
        net = ip((first_int, ip_bits - nbits))
        yield net
        first_int += 1 << nbits
        if first_int - 1 == ip._ALL_ONES:
            break


def _collapse_addresses_internal(addresses):
    """Loops through the addresses, collapsing concurrent netblocks.

    Example:

        ip1 = IPv4Network('192.0.2.0/26')
        ip2 = IPv4Network('192.0.2.64/26')
        ip3 = IPv4Network('192.0.2.128/26')
        ip4 = IPv4Network('192.0.2.192/26')

        _collapse_addresses_internal([ip1, ip2, ip3, ip4]) ->
          [IPv4Network('192.0.2.0/24')]

        This shouldn't be called directly; it is called via
          collapse_addresses([]).

    Args:
        addresses: A list of IPv4Network's or IPv6Network's

    Returns:
        A list of IPv4Network's or IPv6Network's depending on what we were
        passed.

    """
    # First merge
    to_merge = list(addresses)
    subnets = {}
    while to_merge:
        net = to_merge.pop()
        supernet = net.supernet()
        existing = subnets.get(supernet)
        if existing is None:
            subnets[supernet] = net
        elif existing != net:
            # Merge consecutive subnets
            del subnets[supernet]
            to_merge.append(supernet)
    # Then iterate over resulting networks, skipping subsumed subnets
    last = None
    for net in sorted(subnets.values()):
        if last is not None:
            # Since they are sorted,
            # last.network_address <= net.network_address is a given.
            if last.broadcast_address >= net.broadcast_address:
                continue
        yield net
        last = net


def collapse_addresses(addresses):
    """Collapse a list of IP objects.

    Example:
        collapse_addresses([IPv4Network('192.0.2.0/25'),
                            IPv4Network('192.0.2.128/25')]) ->
                           [IPv4Network('192.0.2.0/24')]

    Args:
        addresses: An iterator of IPv4Network or IPv6Network objects.

    Returns:
        An iterator of the collapsed IPv(4|6)Network objects.

    Raises:
        TypeError: If passed a list of mixed version objects.

    """
    addrs = []
    ips = []
    nets = []

    # split IP addresses and networks
    for ip in addresses:
        if isinstance(ip, _BaseAddress):
            if ips and ips[-1]._version != ip._version:
                raise TypeError("%s and %s are not of the same version" % (
                                ip, ips[-1]))
            ips.append(ip)
        elif ip._prefixlen == ip._max_prefixlen:
            if ips and ips[-1]._version != ip._version:
                raise TypeError("%s and %s are not of the same version" % (
                                ip, ips[-1]))
            try:
                ips.append(ip.ip)
            except AttributeError:
                ips.append(ip.network_address)
        else:
            if nets and nets[-1]._version != ip._version:
                raise TypeError("%s and %s are not of the same version" % (
                                ip, nets[-1]))
            nets.append(ip)

    # sort and dedup
    ips = sorted(set(ips))

    # find consecutive address ranges in the sorted sequence and summarize them
    if ips:
        for first, last in _find_address_range(ips):
            addrs.extend(summarize_address_range(first, last))

    return _collapse_addresses_internal(addrs + nets)


def get_mixed_type_key(obj):
    """Return a key suitable for sorting between networks and addresses.

    Address and Network objects are not sortable by default; they're
    fundamentally different so the expression

        IPv4Address('192.0.2.0') <= IPv4Network('192.0.2.0/24')

    doesn't make any sense.  There are some times however, where you may wish
    to have ipaddress sort these for you anyway. If you need to do this, you
    can use this function as the key= argument to sorted().

    Args:
      obj: either a Network or Address object.
    Returns:
      appropriate key.

    """
    if isinstance(obj, _BaseNetwork):
        return obj._get_networks_key()
    elif isinstance(obj, _BaseAddress):
        return obj._get_address_key()
    return NotImplemented


class _IPAddressBase(_TotalOrderingMixin):

    """The mother class."""

    __slots__ = ()

    @property
    def exploded(self):
        """Return the longhand version of the IP address as a string."""
        return self._explode_shorthand_ip_string()

    @property
    def compressed(self):
        """Return the shorthand version of the IP address as a string."""
        return _compat_str(self)

    @property
    def reverse_pointer(self):
        """The name of the reverse DNS pointer for the IP address, e.g.:
            >>> ipaddress.ip_address("127.0.0.1").reverse_pointer
            '1.0.0.127.in-addr.arpa'
            >>> ipaddress.ip_address("2001:db8::1").reverse_pointer
            '1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa'

        """
        return self._reverse_pointer()

    @property
    def version(self):
        msg = '%200s has no version specified' % (type(self),)
        raise NotImplementedError(msg)

    def _check_int_address(self, address):
        if address < 0:
            msg = "%d (< 0) is not permitted as an IPv%d address"
            raise AddressValueError(msg % (address, self._version))
        if address > self._ALL_ONES:
            msg = "%d (>= 2**%d) is not permitted as an IPv%d address"
            raise AddressValueError(msg % (address, self._max_prefixlen,
                                           self._version))

    def _check_packed_address(self, address, expected_len):
        address_len = len(address)
        if address_len != expected_len:
            msg = (
                '%r (len %d != %d) is not permitted as an IPv%d address. '
                'Did you pass in a bytes (str in Python 2) instead of'
                ' a unicode object?'
            )
            raise AddressValueError(msg % (address, address_len,
                                           expected_len, self._version))

    @classmethod
    def _ip_int_from_prefix(cls, prefixlen):
        """Turn the prefix length into a bitwise netmask

        Args:
            prefixlen: An integer, the prefix length.

        Returns:
            An integer.

        """
        return cls._ALL_ONES ^ (cls._ALL_ONES >> prefixlen)

    @classmethod
    def _prefix_from_ip_int(cls, ip_int):
        """Return prefix length from the bitwise netmask.

        Args:
            ip_int: An integer, the netmask in expanded bitwise format

        Returns:
            An integer, the prefix length.

        Raises:
            ValueError: If the input intermingles zeroes & ones
        """
        trailing_zeroes = _count_righthand_zero_bits(ip_int,
                                                     cls._max_prefixlen)
        prefixlen = cls._max_prefixlen - trailing_zeroes
        leading_ones = ip_int >> trailing_zeroes
        all_ones = (1 << prefixlen) - 1
        if leading_ones != all_ones:
            byteslen = cls._max_prefixlen // 8
            details = _compat_to_bytes(ip_int, byteslen, 'big')
            msg = 'Netmask pattern %r mixes zeroes & ones'
            raise ValueError(msg % details)
        return prefixlen

    @classmethod
    def _report_invalid_netmask(cls, netmask_str):
        msg = '%r is not a valid netmask' % netmask_str
        raise NetmaskValueError(msg)

    @classmethod
    def _prefix_from_prefix_string(cls, prefixlen_str):
        """Return prefix length from a numeric string

        Args:
            prefixlen_str: The string to be converted

        Returns:
            An integer, the prefix length.

        Raises:
            NetmaskValueError: If the input is not a valid netmask
        """
        # int allows a leading +/- as well as surrounding whitespace,
        # so we ensure that isn't the case
        if not _BaseV4._DECIMAL_DIGITS.issuperset(prefixlen_str):
            cls._report_invalid_netmask(prefixlen_str)
        try:
            prefixlen = int(prefixlen_str)
        except ValueError:
            cls._report_invalid_netmask(prefixlen_str)
        if not (0 <= prefixlen <= cls._max_prefixlen):
            cls._report_invalid_netmask(prefixlen_str)
        return prefixlen

    @classmethod
    def _prefix_from_ip_string(cls, ip_str):
        """Turn a netmask/hostmask string into a prefix length

        Args:
            ip_str: The netmask/hostmask to be converted

        Returns:
            An integer, the prefix length.

        Raises:
            NetmaskValueError: If the input is not a valid netmask/hostmask
        """
        # Parse the netmask/hostmask like an IP address.
        try:
            ip_int = cls._ip_int_from_string(ip_str)
        except AddressValueError:
            cls._report_invalid_netmask(ip_str)

        # Try matching a netmask (this would be /1*0*/ as a bitwise regexp).
        # Note that the two ambiguous cases (all-ones and all-zeroes) are
        # treated as netmasks.
        try:
            return cls._prefix_from_ip_int(ip_int)
        except ValueError:
            pass

        # Invert the bits, and try matching a /0+1+/ hostmask instead.
        ip_int ^= cls._ALL_ONES
        try:
            return cls._prefix_from_ip_int(ip_int)
        except ValueError:
            cls._report_invalid_netmask(ip_str)

    def __reduce__(self):
        return self.__class__, (_compat_str(self),)


class _BaseAddress(_IPAddressBase):

    """A generic IP object.

    This IP class contains the version independent methods which are
    used by single IP addresses.
    """

    __slots__ = ()

    def __int__(self):
        return self._ip

    def __eq__(self, other):
        try:
            return (self._ip == other._ip and
                    self._version == other._version)
        except AttributeError:
            return NotImplemented

    def __lt__(self, other):
        if not isinstance(other, _IPAddressBase):
            return NotImplemented
        if not isinstance(other, _BaseAddress):
            raise TypeError('%s and %s are not of the same type' % (
                self, other))
        if self._version != other._version:
            raise TypeError('%s and %s are not of the same version' % (
                self, other))
        if self._ip != other._ip:
            return self._ip < other._ip
        return False

    # Shorthand for Integer addition and subtraction. This is not
    # meant to ever support addition/subtraction of addresses.
    def __add__(self, other):
        if not isinstance(other, _compat_int_types):
            return NotImplemented
        return self.__class__(int(self) + other)

    def __sub__(self, other):
        if not isinstance(other, _compat_int_types):
            return NotImplemented
        return self.__class__(int(self) - other)

    def __repr__(self):
        return '%s(%r)' % (self.__class__.__name__, _compat_str(self))

    def __str__(self):
        return _compat_str(self._string_from_ip_int(self._ip))

    def __hash__(self):
        return hash(hex(int(self._ip)))

    def _get_address_key(self):
        return (self._version, self)

    def __reduce__(self):
        return self.__class__, (self._ip,)


class _BaseNetwork(_IPAddressBase):

    """A generic IP network object.

    This IP class contains the version independent methods which are
    used by networks.

    """
    def __init__(self, address):
        self._cache = {}

    def __repr__(self):
        return '%s(%r)' % (self.__class__.__name__, _compat_str(self))

    def __str__(self):
        return '%s/%d' % (self.network_address, self.prefixlen)

    def hosts(self):
        """Generate Iterator over usable hosts in a network.

        This is like __iter__ except it doesn't return the network
        or broadcast addresses.

        """
        network = int(self.network_address)
        broadcast = int(self.broadcast_address)
        for x in _compat_range(network + 1, broadcast):
            yield self._address_class(x)

    def __iter__(self):
        network = int(self.network_address)
        broadcast = int(self.broadcast_address)
        for x in _compat_range(network, broadcast + 1):
            yield self._address_class(x)

    def __getitem__(self, n):
        network = int(self.network_address)
        broadcast = int(self.broadcast_address)
        if n >= 0:
            if network + n > broadcast:
                raise IndexError
            return self._address_class(network + n)
        else:
            n += 1
            if broadcast + n < network:
                raise IndexError
            return self._address_class(broadcast + n)

    def __lt__(self, other):
        if not isinstance(other, _IPAddressBase):
            return NotImplemented
        if not isinstance(other, _BaseNetwork):
            raise TypeError('%s and %s are not of the same type' % (
                            self, other))
        if self._version != other._version:
            raise TypeError('%s and %s are not of the same version' % (
                            self, other))
        if self.network_address != other.network_address:
            return self.network_address < other.network_address
        if self.netmask != other.netmask:
            return self.netmask < other.netmask
        return False

    def __eq__(self, other):
        try:
            return (self._version == other._version and
                    self.network_address == other.network_address and
                    int(self.netmask) == int(other.netmask))
        except AttributeError:
            return NotImplemented

    def __hash__(self):
        return hash(int(self.network_address) ^ int(self.netmask))

    def __contains__(self, other):
        # always false if one is v4 and the other is v6.
        if self._version != other._version:
            return False
        # dealing with another network.
        if isinstance(other, _BaseNetwork):
            return False
        # dealing with another address
        else:
            # address
            return (int(self.network_address) <= int(other._ip) <=
                    int(self.broadcast_address))

    def overlaps(self, other):
        """Tell if self is partly contained in other."""
        return self.network_address in other or (
            self.broadcast_address in other or (
                other.network_address in self or (
                    other.broadcast_address in self)))

    @property
    def broadcast_address(self):
        x = self._cache.get('broadcast_address')
        if x is None:
            x = self._address_class(int(self.network_address) |
                                    int(self.hostmask))
            self._cache['broadcast_address'] = x
        return x

    @property
    def hostmask(self):
        x = self._cache.get('hostmask')
        if x is None:
            x = self._address_class(int(self.netmask) ^ self._ALL_ONES)
            self._cache['hostmask'] = x
        return x

    @property
    def with_prefixlen(self):
        return '%s/%d' % (self.network_address, self._prefixlen)

    @property
    def with_netmask(self):
        return '%s/%s' % (self.network_address, self.netmask)

    @property
    def with_hostmask(self):
        return '%s/%s' % (self.network_address, self.hostmask)

    @property
    def num_addresses(self):
        """Number of hosts in the current subnet."""
        return int(self.broadcast_address) - int(self.network_address) + 1

    @property
    def _address_class(self):
        # Returning bare address objects (rather than interfaces) allows for
        # more consistent behaviour across the network address, broadcast
        # address and individual host addresses.
        msg = '%200s has no associated address class' % (type(self),)
        raise NotImplementedError(msg)

    @property
    def prefixlen(self):
        return self._prefixlen

    def address_exclude(self, other):
        """Remove an address from a larger block.

        For example:

            addr1 = ip_network('192.0.2.0/28')
            addr2 = ip_network('192.0.2.1/32')
            addr1.address_exclude(addr2) =
                [IPv4Network('192.0.2.0/32'), IPv4Network('192.0.2.2/31'),
                IPv4Network('192.0.2.4/30'), IPv4Network('192.0.2.8/29')]

        or IPv6:

            addr1 = ip_network('2001:db8::1/32')
            addr2 = ip_network('2001:db8::1/128')
            addr1.address_exclude(addr2) =
                [ip_network('2001:db8::1/128'),
                ip_network('2001:db8::2/127'),
                ip_network('2001:db8::4/126'),
                ip_network('2001:db8::8/125'),
                ...
                ip_network('2001:db8:8000::/33')]

        Args:
            other: An IPv4Network or IPv6Network object of the same type.

        Returns:
            An iterator of the IPv(4|6)Network objects which is self
            minus other.

        Raises:
            TypeError: If self and other are of differing address
              versions, or if other is not a network object.
            ValueError: If other is not completely contained by self.

        """
        if not self._version == other._version:
            raise TypeError("%s and %s are not of the same version" % (
                            self, other))

        if not isinstance(other, _BaseNetwork):
            raise TypeError("%s is not a network object" % other)

        if not other.subnet_of(self):
            raise ValueError('%s not contained in %s' % (other, self))
        if other == self:
            return

        # Make sure we're comparing the network of other.
        other = other.__class__('%s/%s' % (other.network_address,
                                           other.prefixlen))

        s1, s2 = self.subnets()
        while s1 != other and s2 != other:
            if other.subnet_of(s1):
                yield s2
                s1, s2 = s1.subnets()
            elif other.subnet_of(s2):
                yield s1
                s1, s2 = s2.subnets()
            else:
                # If we got here, there's a bug somewhere.
                raise AssertionError('Error performing exclusion: '
                                     's1: %s s2: %s other: %s' %
                                     (s1, s2, other))
        if s1 == other:
            yield s2
        elif s2 == other:
            yield s1
        else:
            # If we got here, there's a bug somewhere.
            raise AssertionError('Error performing exclusion: '
                                 's1: %s s2: %s other: %s' %
                                 (s1, s2, other))

    def compare_networks(self, other):
        """Compare two IP objects.

        This is only concerned about the comparison of the integer
        representation of the network addresses.  This means that the
        host bits aren't considered at all in this method.  If you want
        to compare host bits, you can easily enough do a
        'HostA._ip < HostB._ip'

        Args:
            other: An IP object.

        Returns:
            If the IP versions of self and other are the same, returns:

            -1 if self < other:
              eg: IPv4Network('192.0.2.0/25') < IPv4Network('192.0.2.128/25')
              IPv6Network('2001:db8::1000/124') <
                  IPv6Network('2001:db8::2000/124')
            0 if self == other
              eg: IPv4Network('192.0.2.0/24') == IPv4Network('192.0.2.0/24')
              IPv6Network('2001:db8::1000/124') ==
                  IPv6Network('2001:db8::1000/124')
            1 if self > other
              eg: IPv4Network('192.0.2.128/25') > IPv4Network('192.0.2.0/25')
                  IPv6Network('2001:db8::2000/124') >
                      IPv6Network('2001:db8::1000/124')

          Raises:
              TypeError if the IP versions are different.

        """
        # does this need to raise a ValueError?
        if self._version != other._version:
            raise TypeError('%s and %s are not of the same type' % (
                            self, other))
        # self._version == other._version below here:
        if self.network_address < other.network_address:
            return -1
        if self.network_address > other.network_address:
            return 1
        # self.network_address == other.network_address below here:
        if self.netmask < other.netmask:
            return -1
        if self.netmask > other.netmask:
            return 1
        return 0

    def _get_networks_key(self):
        """Network-only key function.

        Returns an object that identifies this address' network and
        netmask. This function is a suitable "key" argument for sorted()
        and list.sort().

        """
        return (self._version, self.network_address, self.netmask)

    def subnets(self, prefixlen_diff=1, new_prefix=None):
        """The subnets which join to make the current subnet.

        In the case that self contains only one IP
        (self._prefixlen == 32 for IPv4 or self._prefixlen == 128
        for IPv6), yield an iterator with just ourself.

        Args:
            prefixlen_diff: An integer, the amount the prefix length
              should be increased by. This should not be set if
              new_prefix is also set.
            new_prefix: The desired new prefix length. This must be a
              larger number (smaller prefix) than the existing prefix.
              This should not be set if prefixlen_diff is also set.

        Returns:
            An iterator of IPv(4|6) objects.

        Raises:
            ValueError: The prefixlen_diff is too small or too large.
                OR
            prefixlen_diff and new_prefix are both set or new_prefix
              is a smaller number than the current prefix (smaller
              number means a larger network)

        """
        if self._prefixlen == self._max_prefixlen:
            yield self
            return

        if new_prefix is not None:
            if new_prefix < self._prefixlen:
                raise ValueError('new prefix must be longer')
            if prefixlen_diff != 1:
                raise ValueError('cannot set prefixlen_diff and new_prefix')
            prefixlen_diff = new_prefix - self._prefixlen

        if prefixlen_diff < 0:
            raise ValueError('prefix length diff must be > 0')
        new_prefixlen = self._prefixlen + prefixlen_diff

        if new_prefixlen > self._max_prefixlen:
            raise ValueError(
                'prefix length diff %d is invalid for netblock %s' % (
                    new_prefixlen, self))

        start = int(self.network_address)
        end = int(self.broadcast_address)
        step = (int(self.hostmask) + 1) >> prefixlen_diff
        for new_addr in _compat_range(start, end, step):
            current = self.__class__((new_addr, new_prefixlen))
            yield current

    def supernet(self, prefixlen_diff=1, new_prefix=None):
        """The supernet containing the current network.

        Args:
            prefixlen_diff: An integer, the amount the prefix length of
              the network should be decreased by.  For example, given a
              /24 network and a prefixlen_diff of 3, a supernet with a
              /21 netmask is returned.

        Returns:
            An IPv4 network object.

        Raises:
            ValueError: If self.prefixlen - prefixlen_diff < 0. I.e., you have
              a negative prefix length.
                OR
            If prefixlen_diff and new_prefix are both set or new_prefix is a
              larger number than the current prefix (larger number means a
              smaller network)

        """
        if self._prefixlen == 0:
            return self

        if new_prefix is not None:
            if new_prefix > self._prefixlen:
                raise ValueError('new prefix must be shorter')
            if prefixlen_diff != 1:
                raise ValueError('cannot set prefixlen_diff and new_prefix')
            prefixlen_diff = self._prefixlen - new_prefix

        new_prefixlen = self.prefixlen - prefixlen_diff
        if new_prefixlen < 0:
            raise ValueError(
                'current prefixlen is %d, cannot have a prefixlen_diff of %d' %
                (self.prefixlen, prefixlen_diff))
        return self.__class__((
            int(self.network_address) & (int(self.netmask) << prefixlen_diff),
            new_prefixlen
        ))

    @property
    def is_multicast(self):
        """Test if the address is reserved for multicast use.

        Returns:
            A boolean, True if the address is a multicast address.
            See RFC 2373 2.7 for details.

        """
        return (self.network_address.is_multicast and
                self.broadcast_address.is_multicast)

    def subnet_of(self, other):
        # always false if one is v4 and the other is v6.
        if self._version != other._version:
            return False
        # dealing with another network.
        if (hasattr(other, 'network_address') and
                hasattr(other, 'broadcast_address')):
            return (other.network_address <= self.network_address and
                    other.broadcast_address >= self.broadcast_address)
        # dealing with another address
        else:
            raise TypeError('Unable to test subnet containment with element '
                            'of type %s' % type(other))

    def supernet_of(self, other):
        # always false if one is v4 and the other is v6.
        if self._version != other._version:
            return False
        # dealing with another network.
        if (hasattr(other, 'network_address') and
                hasattr(other, 'broadcast_address')):
            return (other.network_address >= self.network_address and
                    other.broadcast_address <= self.broadcast_address)
        # dealing with another address
        else:
            raise TypeError('Unable to test subnet containment with element '
                            'of type %s' % type(other))

    @property
    def is_reserved(self):
        """Test if the address is otherwise IETF reserved.

        Returns:
            A boolean, True if the address is within one of the
            reserved IPv6 Network ranges.

        """
        return (self.network_address.is_reserved and
                self.broadcast_address.is_reserved)

    @property
    def is_link_local(self):
        """Test if the address is reserved for link-local.

        Returns:
            A boolean, True if the address is reserved per RFC 4291.

        """
        return (self.network_address.is_link_local and
                self.broadcast_address.is_link_local)

    @property
    def is_private(self):
        """Test if this address is allocated for private networks.

        Returns:
            A boolean, True if the address is reserved per
            iana-ipv4-special-registry or iana-ipv6-special-registry.

        """
        return (self.network_address.is_private and
                self.broadcast_address.is_private)

    @property
    def is_global(self):
        """Test if this address is allocated for public networks.

        Returns:
            A boolean, True if the address is not reserved per
            iana-ipv4-special-registry or iana-ipv6-special-registry.

        """
        return not self.is_private

    @property
    def is_unspecified(self):
        """Test if the address is unspecified.

        Returns:
            A boolean, True if this is the unspecified address as defined in
            RFC 2373 2.5.2.

        """
        return (self.network_address.is_unspecified and
                self.broadcast_address.is_unspecified)

    @property
    def is_loopback(self):
        """Test if the address is a loopback address.

        Returns:
            A boolean, True if the address is a loopback address as defined in
            RFC 2373 2.5.3.

        """
        return (self.network_address.is_loopback and
                self.broadcast_address.is_loopback)


class _BaseV4(object):

    """Base IPv4 object.

    The following methods are used by IPv4 objects in both single IP
    addresses and networks.

    """

    __slots__ = ()
    _version = 4
    # Equivalent to 255.255.255.255 or 32 bits of 1's.
    _ALL_ONES = (2 ** IPV4LENGTH) - 1
    _DECIMAL_DIGITS = frozenset('0123456789')

    # the valid octets for host and netmasks. only useful for IPv4.
    _valid_mask_octets = frozenset([255, 254, 252, 248, 240, 224, 192, 128, 0])

    _max_prefixlen = IPV4LENGTH
    # There are only a handful of valid v4 netmasks, so we cache them all
    # when constructed (see _make_netmask()).
    _netmask_cache = {}

    def _explode_shorthand_ip_string(self):
        return _compat_str(self)

    @classmethod
    def _make_netmask(cls, arg):
        """Make a (netmask, prefix_len) tuple from the given argument.

        Argument can be:
        - an integer (the prefix length)
        - a string representing the prefix length (e.g. "24")
        - a string representing the prefix netmask (e.g. "255.255.255.0")
        """
        if arg not in cls._netmask_cache:
            if isinstance(arg, _compat_int_types):
                prefixlen = arg
            else:
                try:
                    # Check for a netmask in prefix length form
                    prefixlen = cls._prefix_from_prefix_string(arg)
                except NetmaskValueError:
                    # Check for a netmask or hostmask in dotted-quad form.
                    # This may raise NetmaskValueError.
                    prefixlen = cls._prefix_from_ip_string(arg)
            netmask = IPv4Address(cls._ip_int_from_prefix(prefixlen))
            cls._netmask_cache[arg] = netmask, prefixlen
        return cls._netmask_cache[arg]

    @classmethod
    def _ip_int_from_string(cls, ip_str):
        """Turn the given IP string into an integer for comparison.

        Args:
            ip_str: A string, the IP ip_str.

        Returns:
            The IP ip_str as an integer.

        Raises:
            AddressValueError: if ip_str isn't a valid IPv4 Address.

        """
        if not ip_str:
            raise AddressValueError('Address cannot be empty')

        octets = ip_str.split('.')
        if len(octets) != 4:
            raise AddressValueError("Expected 4 octets in %r" % ip_str)

        try:
            return _compat_int_from_byte_vals(
                map(cls._parse_octet, octets), 'big')
        except ValueError as exc:
            raise AddressValueError("%s in %r" % (exc, ip_str))

    @classmethod
    def _parse_octet(cls, octet_str):
        """Convert a decimal octet into an integer.

        Args:
            octet_str: A string, the number to parse.

        Returns:
            The octet as an integer.

        Raises:
            ValueError: if the octet isn't strictly a decimal from [0..255].

        """
        if not octet_str:
            raise ValueError("Empty octet not permitted")
        # Whitelist the characters, since int() allows a lot of bizarre stuff.
        if not cls._DECIMAL_DIGITS.issuperset(octet_str):
            msg = "Only decimal digits permitted in %r"
            raise ValueError(msg % octet_str)
        # We do the length check second, since the invalid character error
        # is likely to be more informative for the user
        if len(octet_str) > 3:
            msg = "At most 3 characters permitted in %r"
            raise ValueError(msg % octet_str)
        # Convert to integer (we know digits are legal)
        octet_int = int(octet_str, 10)
        # Any octets that look like they *might* be written in octal,
        # and which don't look exactly the same in both octal and
        # decimal are rejected as ambiguous
        if octet_int > 7 and octet_str[0] == '0':
            msg = "Ambiguous (octal/decimal) value in %r not permitted"
            raise ValueError(msg % octet_str)
        if octet_int > 255:
            raise ValueError("Octet %d (> 255) not permitted" % octet_int)
        return octet_int

    @classmethod
    def _string_from_ip_int(cls, ip_int):
        """Turns a 32-bit integer into dotted decimal notation.

        Args:
            ip_int: An integer, the IP address.

        Returns:
            The IP address as a string in dotted decimal notation.

        """
        return '.'.join(_compat_str(struct.unpack(b'!B', b)[0]
                                    if isinstance(b, bytes)
                                    else b)
                        for b in _compat_to_bytes(ip_int, 4, 'big'))

    def _is_hostmask(self, ip_str):
        """Test if the IP string is a hostmask (rather than a netmask).

        Args:
            ip_str: A string, the potential hostmask.

        Returns:
            A boolean, True if the IP string is a hostmask.

        """
        bits = ip_str.split('.')
        try:
            parts = [x for x in map(int, bits) if x in self._valid_mask_octets]
        except ValueError:
            return False
        if len(parts) != len(bits):
            return False
        if parts[0] < parts[-1]:
            return True
        return False

    def _reverse_pointer(self):
        """Return the reverse DNS pointer name for the IPv4 address.

        This implements the method described in RFC1035 3.5.

        """
        reverse_octets = _compat_str(self).split('.')[::-1]
        return '.'.join(reverse_octets) + '.in-addr.arpa'

    @property
    def max_prefixlen(self):
        return self._max_prefixlen

    @property
    def version(self):
        return self._version


class IPv4Address(_BaseV4, _BaseAddress):

    """Represent and manipulate single IPv4 Addresses."""

    __slots__ = ('_ip', '__weakref__')

    def __init__(self, address):

        """
        Args:
            address: A string or integer representing the IP

              Additionally, an integer can be passed, so
              IPv4Address('192.0.2.1') == IPv4Address(3221225985).
              or, more generally
              IPv4Address(int(IPv4Address('192.0.2.1'))) ==
                IPv4Address('192.0.2.1')

        Raises:
            AddressValueError: If ipaddress isn't a valid IPv4 address.

        """
        # Efficient constructor from integer.
        if isinstance(address, _compat_int_types):
            self._check_int_address(address)
            self._ip = address
            return

        # Constructing from a packed address
        if isinstance(address, bytes):
            self._check_packed_address(address, 4)
            bvs = _compat_bytes_to_byte_vals(address)
            self._ip = _compat_int_from_byte_vals(bvs, 'big')
            return

        # Assume input argument to be string or any object representation
        # which converts into a formatted IP string.
        addr_str = _compat_str(address)
        if '/' in addr_str:
            raise AddressValueError("Unexpected '/' in %r" % address)
        self._ip = self._ip_int_from_string(addr_str)

    @property
    def packed(self):
        """The binary representation of this address."""
        return v4_int_to_packed(self._ip)

    @property
    def is_reserved(self):
        """Test if the address is otherwise IETF reserved.

         Returns:
             A boolean, True if the address is within the
             reserved IPv4 Network range.

        """
        return self in self._constants._reserved_network

    @property
    def is_private(self):
        """Test if this address is allocated for private networks.

        Returns:
            A boolean, True if the address is reserved per
            iana-ipv4-special-registry.

        """
        return any(self in net for net in self._constants._private_networks)

    @property
    def is_multicast(self):
        """Test if the address is reserved for multicast use.

        Returns:
            A boolean, True if the address is multicast.
            See RFC 3171 for details.

        """
        return self in self._constants._multicast_network

    @property
    def is_unspecified(self):
        """Test if the address is unspecified.

        Returns:
            A boolean, True if this is the unspecified address as defined in
            RFC 5735 3.

        """
        return self == self._constants._unspecified_address

    @property
    def is_loopback(self):
        """Test if the address is a loopback address.

        Returns:
            A boolean, True if the address is a loopback per RFC 3330.

        """
        return self in self._constants._loopback_network

    @property
    def is_link_local(self):
        """Test if the address is reserved for link-local.

        Returns:
            A boolean, True if the address is link-local per RFC 3927.

        """
        return self in self._constants._linklocal_network


class IPv4Interface(IPv4Address):

    def __init__(self, address):
        if isinstance(address, (bytes, _compat_int_types)):
            IPv4Address.__init__(self, address)
            self.network = IPv4Network(self._ip)
            self._prefixlen = self._max_prefixlen
            return

        if isinstance(address, tuple):
            IPv4Address.__init__(self, address[0])
            if len(address) > 1:
                self._prefixlen = int(address[1])
            else:
                self._prefixlen = self._max_prefixlen

            self.network = IPv4Network(address, strict=False)
            self.netmask = self.network.netmask
            self.hostmask = self.network.hostmask
            return

        addr = _split_optional_netmask(address)
        IPv4Address.__init__(self, addr[0])

        self.network = IPv4Network(address, strict=False)
        self._prefixlen = self.network._prefixlen

        self.netmask = self.network.netmask
        self.hostmask = self.network.hostmask

    def __str__(self):
        return '%s/%d' % (self._string_from_ip_int(self._ip),
                          self.network.prefixlen)

    def __eq__(self, other):
        address_equal = IPv4Address.__eq__(self, other)
        if not address_equal or address_equal is NotImplemented:
            return address_equal
        try:
            return self.network == other.network
        except AttributeError:
            # An interface with an associated network is NOT the
            # same as an unassociated address. That's why the hash
            # takes the extra info into account.
            return False

    def __lt__(self, other):
        address_less = IPv4Address.__lt__(self, other)
        if address_less is NotImplemented:
            return NotImplemented
        try:
            return self.network < other.network
        except AttributeError:
            # We *do* allow addresses and interfaces to be sorted. The
            # unassociated address is considered less than all interfaces.
            return False

    def __hash__(self):
        return self._ip ^ self._prefixlen ^ int(self.network.network_address)

    __reduce__ = _IPAddressBase.__reduce__

    @property
    def ip(self):
        return IPv4Address(self._ip)

    @property
    def with_prefixlen(self):
        return '%s/%s' % (self._string_from_ip_int(self._ip),
                          self._prefixlen)

    @property
    def with_netmask(self):
        return '%s/%s' % (self._string_from_ip_int(self._ip),
                          self.netmask)

    @property
    def with_hostmask(self):
        return '%s/%s' % (self._string_from_ip_int(self._ip),
                          self.hostmask)


class IPv4Network(_BaseV4, _BaseNetwork):

    """This class represents and manipulates 32-bit IPv4 network + addresses..

    Attributes: [examples for IPv4Network('192.0.2.0/27')]
        .network_address: IPv4Address('192.0.2.0')
        .hostmask: IPv4Address('0.0.0.31')
        .broadcast_address: IPv4Address('192.0.2.32')
        .netmask: IPv4Address('255.255.255.224')
        .prefixlen: 27

    """
    # Class to use when creating address objects
    _address_class = IPv4Address

    def __init__(self, address, strict=True):

        """Instantiate a new IPv4 network object.

        Args:
            address: A string or integer representing the IP [& network].
              '192.0.2.0/24'
              '192.0.2.0/255.255.255.0'
              '192.0.0.2/0.0.0.255'
              are all functionally the same in IPv4. Similarly,
              '192.0.2.1'
              '192.0.2.1/255.255.255.255'
              '192.0.2.1/32'
              are also functionally equivalent. That is to say, failing to
              provide a subnetmask will create an object with a mask of /32.

              If the mask (portion after the / in the argument) is given in
              dotted quad form, it is treated as a netmask if it starts with a
              non-zero field (e.g. /255.0.0.0 == /8) and as a hostmask if it
              starts with a zero field (e.g. 0.255.255.255 == /8), with the
              single exception of an all-zero mask which is treated as a
              netmask == /0. If no mask is given, a default of /32 is used.

              Additionally, an integer can be passed, so
              IPv4Network('192.0.2.1') == IPv4Network(3221225985)
              or, more generally
              IPv4Interface(int(IPv4Interface('192.0.2.1'))) ==
                IPv4Interface('192.0.2.1')

        Raises:
            AddressValueError: If ipaddress isn't a valid IPv4 address.
            NetmaskValueError: If the netmask isn't valid for
              an IPv4 address.
            ValueError: If strict is True and a network address is not
              supplied.

        """
        _BaseNetwork.__init__(self, address)

        # Constructing from a packed address or integer
        if isinstance(address, (_compat_int_types, bytes)):
            self.network_address = IPv4Address(address)
            self.netmask, self._prefixlen = self._make_netmask(
                self._max_prefixlen)
            # fixme: address/network test here.
            return

        if isinstance(address, tuple):
            if len(address) > 1:
                arg = address[1]
            else:
                # We weren't given an address[1]
                arg = self._max_prefixlen
            self.network_address = IPv4Address(address[0])
            self.netmask, self._prefixlen = self._make_netmask(arg)
            packed = int(self.network_address)
            if packed & int(self.netmask) != packed:
                if strict:
                    raise ValueError('%s has host bits set' % self)
                else:
                    self.network_address = IPv4Address(packed &
                                                       int(self.netmask))
            return

        # Assume input argument to be string or any object representation
        # which converts into a formatted IP prefix string.
        addr = _split_optional_netmask(address)
        self.network_address = IPv4Address(self._ip_int_from_string(addr[0]))

        if len(addr) == 2:
            arg = addr[1]
        else:
            arg = self._max_prefixlen
        self.netmask, self._prefixlen = self._make_netmask(arg)

        if strict:
            if (IPv4Address(int(self.network_address) & int(self.netmask)) !=
                    self.network_address):
                raise ValueError('%s has host bits set' % self)
        self.network_address = IPv4Address(int(self.network_address) &
                                           int(self.netmask))

        if self._prefixlen == (self._max_prefixlen - 1):
            self.hosts = self.__iter__

    @property
    def is_global(self):
        """Test if this address is allocated for public networks.

        Returns:
            A boolean, True if the address is not reserved per
            iana-ipv4-special-registry.

        """
        return (not (self.network_address in IPv4Network('100.64.0.0/10') and
                self.broadcast_address in IPv4Network('100.64.0.0/10')) and
                not self.is_private)


class _IPv4Constants(object):

    _linklocal_network = IPv4Network('169.254.0.0/16')

    _loopback_network = IPv4Network('127.0.0.0/8')

    _multicast_network = IPv4Network('224.0.0.0/4')

    _private_networks = [
        IPv4Network('0.0.0.0/8'),
        IPv4Network('10.0.0.0/8'),
        IPv4Network('127.0.0.0/8'),
        IPv4Network('169.254.0.0/16'),
        IPv4Network('172.16.0.0/12'),
        IPv4Network('192.0.0.0/29'),
        IPv4Network('192.0.0.170/31'),
        IPv4Network('192.0.2.0/24'),
        IPv4Network('192.168.0.0/16'),
        IPv4Network('198.18.0.0/15'),
        IPv4Network('198.51.100.0/24'),
        IPv4Network('203.0.113.0/24'),
        IPv4Network('240.0.0.0/4'),
        IPv4Network('255.255.255.255/32'),
    ]

    _reserved_network = IPv4Network('240.0.0.0/4')

    _unspecified_address = IPv4Address('0.0.0.0')


IPv4Address._constants = _IPv4Constants


class _BaseV6(object):

    """Base IPv6 object.

    The following methods are used by IPv6 objects in both single IP
    addresses and networks.

    """

    __slots__ = ()
    _version = 6
    _ALL_ONES = (2 ** IPV6LENGTH) - 1
    _HEXTET_COUNT = 8
    _HEX_DIGITS = frozenset('0123456789ABCDEFabcdef')
    _max_prefixlen = IPV6LENGTH

    # There are only a bunch of valid v6 netmasks, so we cache them all
    # when constructed (see _make_netmask()).
    _netmask_cache = {}

    @classmethod
    def _make_netmask(cls, arg):
        """Make a (netmask, prefix_len) tuple from the given argument.

        Argument can be:
        - an integer (the prefix length)
        - a string representing the prefix length (e.g. "24")
        - a string representing the prefix netmask (e.g. "255.255.255.0")
        """
        if arg not in cls._netmask_cache:
            if isinstance(arg, _compat_int_types):
                prefixlen = arg
            else:
                prefixlen = cls._prefix_from_prefix_string(arg)
            netmask = IPv6Address(cls._ip_int_from_prefix(prefixlen))
            cls._netmask_cache[arg] = netmask, prefixlen
        return cls._netmask_cache[arg]

    @classmethod
    def _ip_int_from_string(cls, ip_str):
        """Turn an IPv6 ip_str into an integer.

        Args:
            ip_str: A string, the IPv6 ip_str.

        Returns:
            An int, the IPv6 address

        Raises:
            AddressValueError: if ip_str isn't a valid IPv6 Address.

        """
        if not ip_str:
            raise AddressValueError('Address cannot be empty')

        parts = ip_str.split(':')

        # An IPv6 address needs at least 2 colons (3 parts).
        _min_parts = 3
        if len(parts) < _min_parts:
            msg = "At least %d parts expected in %r" % (_min_parts, ip_str)
            raise AddressValueError(msg)

        # If the address has an IPv4-style suffix, convert it to hexadecimal.
        if '.' in parts[-1]:
            try:
                ipv4_int = IPv4Address(parts.pop())._ip
            except AddressValueError as exc:
                raise AddressValueError("%s in %r" % (exc, ip_str))
            parts.append('%x' % ((ipv4_int >> 16) & 0xFFFF))
            parts.append('%x' % (ipv4_int & 0xFFFF))

        # An IPv6 address can't have more than 8 colons (9 parts).
        # The extra colon comes from using the "::" notation for a single
        # leading or trailing zero part.
        _max_parts = cls._HEXTET_COUNT + 1
        if len(parts) > _max_parts:
            msg = "At most %d colons permitted in %r" % (
                _max_parts - 1, ip_str)
            raise AddressValueError(msg)

        # Disregarding the endpoints, find '::' with nothing in between.
        # This indicates that a run of zeroes has been skipped.
        skip_index = None
        for i in _compat_range(1, len(parts) - 1):
            if not parts[i]:
                if skip_index is not None:
                    # Can't have more than one '::'
                    msg = "At most one '::' permitted in %r" % ip_str
                    raise AddressValueError(msg)
                skip_index = i

        # parts_hi is the number of parts to copy from above/before the '::'
        # parts_lo is the number of parts to copy from below/after the '::'
        if skip_index is not None:
            # If we found a '::', then check if it also covers the endpoints.
            parts_hi = skip_index
            parts_lo = len(parts) - skip_index - 1
            if not parts[0]:
                parts_hi -= 1
                if parts_hi:
                    msg = "Leading ':' only permitted as part of '::' in %r"
                    raise AddressValueError(msg % ip_str)  # ^: requires ^::
            if not parts[-1]:
                parts_lo -= 1
                if parts_lo:
                    msg = "Trailing ':' only permitted as part of '::' in %r"
                    raise AddressValueError(msg % ip_str)  # :$ requires ::$
            parts_skipped = cls._HEXTET_COUNT - (parts_hi + parts_lo)
            if parts_skipped < 1:
                msg = "Expected at most %d other parts with '::' in %r"
                raise AddressValueError(msg % (cls._HEXTET_COUNT - 1, ip_str))
        else:
            # Otherwise, allocate the entire address to parts_hi.  The
            # endpoints could still be empty, but _parse_hextet() will check
            # for that.
            if len(parts) != cls._HEXTET_COUNT:
                msg = "Exactly %d parts expected without '::' in %r"
                raise AddressValueError(msg % (cls._HEXTET_COUNT, ip_str))
            if not parts[0]:
                msg = "Leading ':' only permitted as part of '::' in %r"
                raise AddressValueError(msg % ip_str)  # ^: requires ^::
            if not parts[-1]:
                msg = "Trailing ':' only permitted as part of '::' in %r"
                raise AddressValueError(msg % ip_str)  # :$ requires ::$
            parts_hi = len(parts)
            parts_lo = 0
            parts_skipped = 0

        try:
            # Now, parse the hextets into a 128-bit integer.
            ip_int = 0
            for i in range(parts_hi):
                ip_int <<= 16
                ip_int |= cls._parse_hextet(parts[i])
            ip_int <<= 16 * parts_skipped
            for i in range(-parts_lo, 0):
                ip_int <<= 16
                ip_int |= cls._parse_hextet(parts[i])
            return ip_int
        except ValueError as exc:
            raise AddressValueError("%s in %r" % (exc, ip_str))

    @classmethod
    def _parse_hextet(cls, hextet_str):
        """Convert an IPv6 hextet string into an integer.

        Args:
            hextet_str: A string, the number to parse.

        Returns:
            The hextet as an integer.

        Raises:
            ValueError: if the input isn't strictly a hex number from
              [0..FFFF].

        """
        # Whitelist the characters, since int() allows a lot of bizarre stuff.
        if not cls._HEX_DIGITS.issuperset(hextet_str):
            raise ValueError("Only hex digits permitted in %r" % hextet_str)
        # We do the length check second, since the invalid character error
        # is likely to be more informative for the user
        if len(hextet_str) > 4:
            msg = "At most 4 characters permitted in %r"
            raise ValueError(msg % hextet_str)
        # Length check means we can skip checking the integer value
        return int(hextet_str, 16)

    @classmethod
    def _compress_hextets(cls, hextets):
        """Compresses a list of hextets.

        Compresses a list of strings, replacing the longest continuous
        sequence of "0" in the list with "" and adding empty strings at
        the beginning or at the end of the string such that subsequently
        calling ":".join(hextets) will produce the compressed version of
        the IPv6 address.

        Args:
            hextets: A list of strings, the hextets to compress.

        Returns:
            A list of strings.

        """
        best_doublecolon_start = -1
        best_doublecolon_len = 0
        doublecolon_start = -1
        doublecolon_len = 0
        for index, hextet in enumerate(hextets):
            if hextet == '0':
                doublecolon_len += 1
                if doublecolon_start == -1:
                    # Start of a sequence of zeros.
                    doublecolon_start = index
                if doublecolon_len > best_doublecolon_len:
                    # This is the longest sequence of zeros so far.
                    best_doublecolon_len = doublecolon_len
                    best_doublecolon_start = doublecolon_start
            else:
                doublecolon_len = 0
                doublecolon_start = -1

        if best_doublecolon_len > 1:
            best_doublecolon_end = (best_doublecolon_start +
                                    best_doublecolon_len)
            # For zeros at the end of the address.
            if best_doublecolon_end == len(hextets):
                hextets += ['']
            hextets[best_doublecolon_start:best_doublecolon_end] = ['']
            # For zeros at the beginning of the address.
            if best_doublecolon_start == 0:
                hextets = [''] + hextets

        return hextets

    @classmethod
    def _string_from_ip_int(cls, ip_int=None):
        """Turns a 128-bit integer into hexadecimal notation.

        Args:
            ip_int: An integer, the IP address.

        Returns:
            A string, the hexadecimal representation of the address.

        Raises:
            ValueError: The address is bigger than 128 bits of all ones.

        """
        if ip_int is None:
            ip_int = int(cls._ip)

        if ip_int > cls._ALL_ONES:
            raise ValueError('IPv6 address is too large')

        hex_str = '%032x' % ip_int
        hextets = ['%x' % int(hex_str[x:x + 4], 16) for x in range(0, 32, 4)]

        hextets = cls._compress_hextets(hextets)
        return ':'.join(hextets)

    def _explode_shorthand_ip_string(self):
        """Expand a shortened IPv6 address.

        Args:
            ip_str: A string, the IPv6 address.

        Returns:
            A string, the expanded IPv6 address.

        """
        if isinstance(self, IPv6Network):
            ip_str = _compat_str(self.network_address)
        elif isinstance(self, IPv6Interface):
            ip_str = _compat_str(self.ip)
        else:
            ip_str = _compat_str(self)

        ip_int = self._ip_int_from_string(ip_str)
        hex_str = '%032x' % ip_int
        parts = [hex_str[x:x + 4] for x in range(0, 32, 4)]
        if isinstance(self, (_BaseNetwork, IPv6Interface)):
            return '%s/%d' % (':'.join(parts), self._prefixlen)
        return ':'.join(parts)

    def _reverse_pointer(self):
        """Return the reverse DNS pointer name for the IPv6 address.

        This implements the method described in RFC3596 2.5.

        """
        reverse_chars = self.exploded[::-1].replace(':', '')
        return '.'.join(reverse_chars) + '.ip6.arpa'

    @property
    def max_prefixlen(self):
        return self._max_prefixlen

    @property
    def version(self):
        return self._version


class IPv6Address(_BaseV6, _BaseAddress):

    """Represent and manipulate single IPv6 Addresses."""

    __slots__ = ('_ip', '__weakref__')

    def __init__(self, address):
        """Instantiate a new IPv6 address object.

        Args:
            address: A string or integer representing the IP

              Additionally, an integer can be passed, so
              IPv6Address('2001:db8::') ==
                IPv6Address(42540766411282592856903984951653826560)
              or, more generally
              IPv6Address(int(IPv6Address('2001:db8::'))) ==
                IPv6Address('2001:db8::')

        Raises:
            AddressValueError: If address isn't a valid IPv6 address.

        """
        # Efficient constructor from integer.
        if isinstance(address, _compat_int_types):
            self._check_int_address(address)
            self._ip = address
            return

        # Constructing from a packed address
        if isinstance(address, bytes):
            self._check_packed_address(address, 16)
            bvs = _compat_bytes_to_byte_vals(address)
            self._ip = _compat_int_from_byte_vals(bvs, 'big')
            return

        # Assume input argument to be string or any object representation
        # which converts into a formatted IP string.
        addr_str = _compat_str(address)
        if '/' in addr_str:
            raise AddressValueError("Unexpected '/' in %r" % address)
        self._ip = self._ip_int_from_string(addr_str)

    @property
    def packed(self):
        """The binary representation of this address."""
        return v6_int_to_packed(self._ip)

    @property
    def is_multicast(self):
        """Test if the address is reserved for multicast use.

        Returns:
            A boolean, True if the address is a multicast address.
            See RFC 2373 2.7 for details.

        """
        return self in self._constants._multicast_network

    @property
    def is_reserved(self):
        """Test if the address is otherwise IETF reserved.

        Returns:
            A boolean, True if the address is within one of the
            reserved IPv6 Network ranges.

        """
        return any(self in x for x in self._constants._reserved_networks)

    @property
    def is_link_local(self):
        """Test if the address is reserved for link-local.

        Returns:
            A boolean, True if the address is reserved per RFC 4291.

        """
        return self in self._constants._linklocal_network

    @property
    def is_site_local(self):
        """Test if the address is reserved for site-local.

        Note that the site-local address space has been deprecated by RFC 3879.
        Use is_private to test if this address is in the space of unique local
        addresses as defined by RFC 4193.

        Returns:
            A boolean, True if the address is reserved per RFC 3513 2.5.6.

        """
        return self in self._constants._sitelocal_network

    @property
    def is_private(self):
        """Test if this address is allocated for private networks.

        Returns:
            A boolean, True if the address is reserved per
            iana-ipv6-special-registry.

        """
        return any(self in net for net in self._constants._private_networks)

    @property
    def is_global(self):
        """Test if this address is allocated for public networks.

        Returns:
            A boolean, true if the address is not reserved per
            iana-ipv6-special-registry.

        """
        return not self.is_private

    @property
    def is_unspecified(self):
        """Test if the address is unspecified.

        Returns:
            A boolean, True if this is the unspecified address as defined in
            RFC 2373 2.5.2.

        """
        return self._ip == 0

    @property
    def is_loopback(self):
        """Test if the address is a loopback address.

        Returns:
            A boolean, True if the address is a loopback address as defined in
            RFC 2373 2.5.3.

        """
        return self._ip == 1

    @property
    def ipv4_mapped(self):
        """Return the IPv4 mapped address.

        Returns:
            If the IPv6 address is a v4 mapped address, return the
            IPv4 mapped address. Return None otherwise.

        """
        if (self._ip >> 32) != 0xFFFF:
            return None
        return IPv4Address(self._ip & 0xFFFFFFFF)

    @property
    def teredo(self):
        """Tuple of embedded teredo IPs.

        Returns:
            Tuple of the (server, client) IPs or None if the address
            doesn't appear to be a teredo address (doesn't start with
            2001::/32)

        """
        if (self._ip >> 96) != 0x20010000:
            return None
        return (IPv4Address((self._ip >> 64) & 0xFFFFFFFF),
                IPv4Address(~self._ip & 0xFFFFFFFF))

    @property
    def sixtofour(self):
        """Return the IPv4 6to4 embedded address.

        Returns:
            The IPv4 6to4-embedded address if present or None if the
            address doesn't appear to contain a 6to4 embedded address.

        """
        if (self._ip >> 112) != 0x2002:
            return None
        return IPv4Address((self._ip >> 80) & 0xFFFFFFFF)


class IPv6Interface(IPv6Address):

    def __init__(self, address):
        if isinstance(address, (bytes, _compat_int_types)):
            IPv6Address.__init__(self, address)
            self.network = IPv6Network(self._ip)
            self._prefixlen = self._max_prefixlen
            return
        if isinstance(address, tuple):
            IPv6Address.__init__(self, address[0])
            if len(address) > 1:
                self._prefixlen = int(address[1])
            else:
                self._prefixlen = self._max_prefixlen
            self.network = IPv6Network(address, strict=False)
            self.netmask = self.network.netmask
            self.hostmask = self.network.hostmask
            return

        addr = _split_optional_netmask(address)
        IPv6Address.__init__(self, addr[0])
        self.network = IPv6Network(address, strict=False)
        self.netmask = self.network.netmask
        self._prefixlen = self.network._prefixlen
        self.hostmask = self.network.hostmask

    def __str__(self):
        return '%s/%d' % (self._string_from_ip_int(self._ip),
                          self.network.prefixlen)

    def __eq__(self, other):
        address_equal = IPv6Address.__eq__(self, other)
        if not address_equal or address_equal is NotImplemented:
            return address_equal
        try:
            return self.network == other.network
        except AttributeError:
            # An interface with an associated network is NOT the
            # same as an unassociated address. That's why the hash
            # takes the extra info into account.
            return False

    def __lt__(self, other):
        address_less = IPv6Address.__lt__(self, other)
        if address_less is NotImplemented:
            return NotImplemented
        try:
            return self.network < other.network
        except AttributeError:
            # We *do* allow addresses and interfaces to be sorted. The
            # unassociated address is considered less than all interfaces.
            return False

    def __hash__(self):
        return self._ip ^ self._prefixlen ^ int(self.network.network_address)

    __reduce__ = _IPAddressBase.__reduce__

    @property
    def ip(self):
        return IPv6Address(self._ip)

    @property
    def with_prefixlen(self):
        return '%s/%s' % (self._string_from_ip_int(self._ip),
                          self._prefixlen)

    @property
    def with_netmask(self):
        return '%s/%s' % (self._string_from_ip_int(self._ip),
                          self.netmask)

    @property
    def with_hostmask(self):
        return '%s/%s' % (self._string_from_ip_int(self._ip),
                          self.hostmask)

    @property
    def is_unspecified(self):
        return self._ip == 0 and self.network.is_unspecified

    @property
    def is_loopback(self):
        return self._ip == 1 and self.network.is_loopback


class IPv6Network(_BaseV6, _BaseNetwork):

    """This class represents and manipulates 128-bit IPv6 networks.

    Attributes: [examples for IPv6('2001:db8::1000/124')]
        .network_address: IPv6Address('2001:db8::1000')
        .hostmask: IPv6Address('::f')
        .broadcast_address: IPv6Address('2001:db8::100f')
        .netmask: IPv6Address('ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff0')
        .prefixlen: 124

    """

    # Class to use when creating address objects
    _address_class = IPv6Address

    def __init__(self, address, strict=True):
        """Instantiate a new IPv6 Network object.

        Args:
            address: A string or integer representing the IPv6 network or the
              IP and prefix/netmask.
              '2001:db8::/128'
              '2001:db8:0000:0000:0000:0000:0000:0000/128'
              '2001:db8::'
              are all functionally the same in IPv6.  That is to say,
              failing to provide a subnetmask will create an object with
              a mask of /128.

              Additionally, an integer can be passed, so
              IPv6Network('2001:db8::') ==
                IPv6Network(42540766411282592856903984951653826560)
              or, more generally
              IPv6Network(int(IPv6Network('2001:db8::'))) ==
                IPv6Network('2001:db8::')

            strict: A boolean. If true, ensure that we have been passed
              A true network address, eg, 2001:db8::1000/124 and not an
              IP address on a network, eg, 2001:db8::1/124.

        Raises:
            AddressValueError: If address isn't a valid IPv6 address.
            NetmaskValueError: If the netmask isn't valid for
              an IPv6 address.
            ValueError: If strict was True and a network address was not
              supplied.

        """
        _BaseNetwork.__init__(self, address)

        # Efficient constructor from integer or packed address
        if isinstance(address, (bytes, _compat_int_types)):
            self.network_address = IPv6Address(address)
            self.netmask, self._prefixlen = self._make_netmask(
                self._max_prefixlen)
            return

        if isinstance(address, tuple):
            if len(address) > 1:
                arg = address[1]
            else:
                arg = self._max_prefixlen
            self.netmask, self._prefixlen = self._make_netmask(arg)
            self.network_address = IPv6Address(address[0])
            packed = int(self.network_address)
            if packed & int(self.netmask) != packed:
                if strict:
                    raise ValueError('%s has host bits set' % self)
                else:
                    self.network_address = IPv6Address(packed &
                                                       int(self.netmask))
            return

        # Assume input argument to be string or any object representation
        # which converts into a formatted IP prefix string.
        addr = _split_optional_netmask(address)

        self.network_address = IPv6Address(self._ip_int_from_string(addr[0]))

        if len(addr) == 2:
            arg = addr[1]
        else:
            arg = self._max_prefixlen
        self.netmask, self._prefixlen = self._make_netmask(arg)

        if strict:
            if (IPv6Address(int(self.network_address) & int(self.netmask)) !=
                    self.network_address):
                raise ValueError('%s has host bits set' % self)
        self.network_address = IPv6Address(int(self.network_address) &
                                           int(self.netmask))

        if self._prefixlen == (self._max_prefixlen - 1):
            self.hosts = self.__iter__

    def hosts(self):
        """Generate Iterator over usable hosts in a network.

          This is like __iter__ except it doesn't return the
          Subnet-Router anycast address.

        """
        network = int(self.network_address)
        broadcast = int(self.broadcast_address)
        for x in _compat_range(network + 1, broadcast + 1):
            yield self._address_class(x)

    @property
    def is_site_local(self):
        """Test if the address is reserved for site-local.

        Note that the site-local address space has been deprecated by RFC 3879.
        Use is_private to test if this address is in the space of unique local
        addresses as defined by RFC 4193.

        Returns:
            A boolean, True if the address is reserved per RFC 3513 2.5.6.

        """
        return (self.network_address.is_site_local and
                self.broadcast_address.is_site_local)


class _IPv6Constants(object):

    _linklocal_network = IPv6Network('fe80::/10')

    _multicast_network = IPv6Network('ff00::/8')

    _private_networks = [
        IPv6Network('::1/128'),
        IPv6Network('::/128'),
        IPv6Network('::ffff:0:0/96'),
        IPv6Network('100::/64'),
        IPv6Network('2001::/23'),
        IPv6Network('2001:2::/48'),
        IPv6Network('2001:db8::/32'),
        IPv6Network('2001:10::/28'),
        IPv6Network('fc00::/7'),
        IPv6Network('fe80::/10'),
    ]

    _reserved_networks = [
        IPv6Network('::/8'), IPv6Network('100::/8'),
        IPv6Network('200::/7'), IPv6Network('400::/6'),
        IPv6Network('800::/5'), IPv6Network('1000::/4'),
        IPv6Network('4000::/3'), IPv6Network('6000::/3'),
        IPv6Network('8000::/3'), IPv6Network('A000::/3'),
        IPv6Network('C000::/3'), IPv6Network('E000::/4'),
        IPv6Network('F000::/5'), IPv6Network('F800::/6'),
        IPv6Network('FE00::/9'),
    ]

    _sitelocal_network = IPv6Network('fec0::/10')


IPv6Address._constants = _IPv6Constants
0707010005c8c6000041ed0000000000000000000000025ab2183a00000000000000660000000000000000000000000000002600000000root/usr/share/opensvc/lib/json_delta 0707010005c8c7000081b40000000000000000000000015ab21754000023f8000000660000000000000000000000000000003200000000root/usr/share/opensvc/lib/json_delta/__init__.py # -*- encoding: utf-8 -*-
# json_delta: a library for computing deltas between JSON-serializable
# structures.
# json_delta/__init__.py
#
# Copyright 2012â€’2015 Philip J. Roberts <himself@phil-roberts.name>.
# BSD License applies; see the LICENSE file, or
# http://opensource.org/licenses/BSD-2-Clause
'''This is the main â€œlibraryâ€ for json_deltaâ€™s functionality.  Functions
available within the namespace of this module are to be considered
part of json_deltaâ€™s stable API, subject to change only after a lot
of noisy announcements and gnashing of teeth.

The names of submodules begin with underscores because the same is not
true of them: the functionality behind the main entry points
:func:`diff`, :func:`patch`, :func:`udiff`, :func:`upatch` may be
refactored at any time.

Requires Python 2.7 or newer (including Python 3).

'''
from __future__ import print_function, unicode_literals
import sys

__VERSION__ = '2.0'

from ._diff import diff as _diff_func
from ._patch import patch
from ._udiff import udiff
from ._upatch import upatch

from ._util import _load_and_func


def diff(left_struc, right_struc, minimal=None, verbose=True, key=None,
         array_align=True, compare_lengths=True, common_key_threshold=0.0):
    '''Compose a sequence of diff stanzas sufficient to convert the
    structure ``left_struc`` into the structure ``right_struc``.  (The
    goal is to add 'necessary and' to 'sufficient' above!).

    Optional parameters:
        ``verbose``: Print compression statistics to
        stderr, and warn if the setting of ``minimal`` contradicts the
        other parms.

        ``array_align``: Use :py:func:`_diff.needle_diff` to compute
        deltas between arrays.  Relatively computationally expensive,
        but likely to produce shorter diffs.  Defaults to ``True``.

        ``compare_lengths``: If ``[[key, right_struc]]`` can be
        encoded as a shorter JSON-string, return it instead of
        examining the internal structure of ``left_struc`` and
        ``right_struc``.  It involves calling :py:func:`json.dumps`
        twice for every node in the structure, but may result in
        smaller diffs.  Defaults to ``True``.

        ``common_key_threshold``: Skip recursion into ``left_struc`` and
        ``right_struc`` if the fraction of keys they have in common
        (with the same value) is less than this parm (which should be a
        float between ``0.0`` and ``1.0``).  Defaults to 0.0.

        ``minimal``: Included for backwards compatibility.  ``True`` is
        equivalent to ``(array_align=True, compare_lengths=True,
        common_key_threshold=0.0)``; ``False`` is equivalent to
        ``(array_align=False, compare_lengths=False,
        common_key_threshold=0.5)``.  Specific settings of
        ``array_align``, ``compare_lengths`` or ``common_key_threshold``
        will supersede this parm, warning on stderr if ``verbose`` and
        ``minimal`` are both set.

        ``key``: Also included for backwards compatibility.  If set,
        will be prepended to the key in each stanza of the output.

    The parameter ``key`` is present because this function is mutually
    recursive with :func:`_diff.needle_diff` and :func:`_diff.keyset_diff`.
    If set to a list, it will be prefixed to every keypath in the
    output.

    '''
    return _diff_func(left_struc, right_struc, verbose=verbose, key=key,
                      **_check_diff_parms(vars()))


def _check_diff_parms(parms):
    '''Resolve parameters to :py:func:`diff`

    Output is a dict suitable for ``**`` passing to :func:`_diff.diff`.

    '''
    options = dict((k, parms[k]) for k in ('array_align', 'compare_lengths',
                                     'common_key_threshold')
               if parms[k] is not None)
    if parms['minimal'] is None:
        return options

    if parms['minimal']:
        defaults = {'array_align': True, 'compare_lengths': True,
                    'common_key_threshold': 0.0}
    else:
        defaults = {'array_align': False, 'compare_lengths': False,
                    'common_key_threshold': 0.5}

    if ((not set(defaults.items()).issuperset(options.items()))
        and parms['verbose']):
        defaults.update(options)
        print('''Warning: arguments contradict one another!  Using the following parms:
\tarray_align: {array_align}
\tcompare_lengths: {compare_lengths}
\tcommon_key_threshold: {common_key_threshold}'''.format(**defaults),
              file=sys.stderr)
    else:
        defaults.update(options)
    return defaults


def load_and_diff(left=None, right=None, both=None, array_align=None,
                  compare_lengths=None, common_key_threshold=None,
                  minimal=None, verbose=True):
    '''Apply :py:func:`diff` to strings or files representing
    JSON-serialized structures.

    Specify either ``left`` and ``right``, or ``both``, like so:

    >>> (load_and_diff('{"foo":"bar"}', '{"foo":"baz"}', verbose=False)
    ...  == [[["foo"],"baz"]])
    True
    >>> (load_and_diff(both='[{"foo":"bar"},{"foo":"baz"}]', verbose=False)
    ...  == [[["foo"],"baz"]])
    True

    ``left``, ``right`` and ``both`` may be either strings (instances
    of `basestring` in 2.7) or file-like objects.

    ``minimal`` and ``verbose`` are passed through to :py:func:`diff`,
    which see.

    A call to this function with string arguments is strictly
    equivalent to calling ``diff(json.loads(left), json.loads(right),
    minimal=minimal, verbose=verbose)`` or ``diff(*json.loads(both),
    minimal=minimal, verbose=verbose)``, as appropriate.
    '''
    return _load_and_func(diff, left, right, both,
                          array_align=array_align,
                          compare_lengths=compare_lengths,
                          common_key_threshold=common_key_threshold,
                          minimal=minimal, verbose=verbose)


def load_and_patch(struc=None, stanzas=None, both=None):
    '''Apply :py:func:`patch` to strings or files representing
    JSON-serialized structures.

    Specify either ``struc`` and ``stanzas``, or ``both``, like so:

    >>> (load_and_patch('{"foo":"bar"}', '[[["foo"],"baz"]]') ==
    ...  {"foo": "baz"})
    True
    >>> (load_and_patch(both='[{"foo":"bar"},[[["foo"],"baz"]]]') ==
    ...  {"foo": "baz"})
    True

    ``struc``, ``stanzas`` and ``both`` may be either strings (instances
    of `basestring` in 2.7) or file-like objects.

    A call to this function with string arguments is strictly
    equivalent to calling ``patch(json.loads(struc), json.loads(stanzas),
    in_place=in_place)`` or ``patch(*json.loads(both),
    in_place=in_place)``, as appropriate.
    '''
    return _load_and_func(patch, struc, stanzas, both)


def load_and_udiff(left=None, right=None, both=None,
                   stanzas=None, indent=0):
    '''Apply :py:func:`udiff` to strings representing JSON-serialized
    structures.

    Specify either ``left`` and ``right``, or ``both``, like so:

    >>> udiff = """ {
    ...  "foo":
    ... -  "bar"
    ... +  "baz"
    ...  }"""
    >>> test = load_and_udiff('{"foo":"bar"}', '{"foo":"baz"}')
    >>> '\\n'.join(test) == udiff
    True
    >>> test = load_and_udiff(both='[{"foo":"bar"},{"foo":"baz"}]')
    >>> '\\n'.join(test) == udiff
    True

    ``left``, ``right`` and ``both`` may be either strings (instances
    of `basestring` in 2.7) or file-like objects.

    ``stanzas`` and ``indent`` are passed through to :py:func:`udiff`,
    which see.

    A call to this function with string arguments is strictly
    equivalent to calling ``udiff(json.loads(left), json.loads(right),
    stanzas=stanzas, indent=indent)`` or ``udiff(*json.loads(both),
    stanzas=stanzas, indent=indent)``, as appropriate.
    '''
    return _load_and_func(udiff, left, right, both,
                          patch=stanzas, indent=indent)


def load_and_upatch(struc=None, json_udiff=None, both=None,
                    reverse=False):
    """Apply :py:func:`upatch` to strings representing JSON-serialized
    structures.

    Specify either ``struc`` and ``json_udiff``, or ``both``, like so:

    >>> struc = '{"foo":"bar"}'
    >>> json_udiff = r'" {\\n  \\"foo\\":\\n-  \\"bar\\"\\n+  \\"baz\\"\\n }"'
    >>> both = r'[{"foo":"baz"}," '\\
    ... r'{\\n  \\"foo\\":\\n-  \\"bar\\"\\n+  \\"baz\\"\\n }"]'
    >>> load_and_upatch(struc, json_udiff) == {"foo": "baz"}
    True
    >>> load_and_upatch(both=both, reverse=True) == {"foo": "bar"}
    True

    ``struc``, ``json_udiff`` and ``both`` may be either strings
    (instances of `basestring` in 2.7) or file-like objects.  Note
    that ``json_udiff`` is so named because it must be a
    JSON-serialized representation of the udiff string, not the udiff
    string itself.

    ``reverse`` is passed through to :py:func:`upatch`, which see.

    A call to this function with string arguments is strictly
    equivalent to calling ``upatch(json.loads(struc),
    json.loads(json_udiff), reverse=reverse, in_place=in_place)`` or
    ``upatch(*json.loads(both), reverse=reverse, in_place=in_place)``,
    as appropriate.

    """
    return _load_and_func(upatch, struc, json_udiff, both, reverse=reverse)
0707010005c8c8000081b40000000000000000000000015ab21754000040c2000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/json_delta/_diff.py    # -*- encoding: utf-8 -*-
# json_delta: a library for computing deltas between JSON-serializable
# structures.
# json_delta/_diff.py
#
# Copyright 2012â€’2015 Philip J. Roberts <himself@phil-roberts.name>.
# BSD License applies; see the LICENSE file, or
# http://opensource.org/licenses/BSD-2-Clause
'''Functions for computing JSON-format diffs.'''

from __future__ import print_function, unicode_literals
from ._util import compact_json_dumps, TERMINALS, NONTERMINALS
from ._util import follow_path, in_array, in_object, range

import copy
import bisect
import sys
import json


def diff(left_struc, right_struc,
         array_align=True, compare_lengths=True, common_key_threshold=0.0,
         verbose=True, key=None):
    '''Compose a sequence of diff stanzas sufficient to convert the
    structure ``left_struc`` into the structure ``right_struc``.
    (Whether you can add â€˜necessary andâ€™ to â€˜sufficient toâ€™ depends on
    the setting of the other parms, and how many cycles you want to
    burn; see below).

    Optional parameters:
        ``array_align``: Use :py:func:`needle_diff` to compute deltas
        between arrays.  Computationally expensive, but likely to
        produce shorter diffs.  If this parm is set to the string
        ``'udiff'``, :py:func:`needle_diff` will optimize for the
        shortest udiff, instead of the shortest JSON-format diff.
        Otherwise, set to any value that is true in a Boolean context
        to enable.

        ``compare_lengths``: If ``[[key, right_struc]]`` can be encoded
        as a shorter JSON-string, return it instead of examining the
        internal structure of ``left_struc`` and ``right_struc``.  It
        involves calling :func:`json.dumps` twice for every node in
        the structure, but may result in smaller diffs.

        ``common_key_threshold``: Skip recursion into ``left_struc``
        and ``right_struc`` if the fraction of keys they have in
        common (as computed by :func:`commonality`, which see) is less
        than this parm (which should be a float between ``0.0`` and
        ``1.0``).

        ``verbose``: Print compression statistics will be to stderr.

    The parameter ``key`` is present because this function is mutually
    recursive with :py:func:`needle_diff` and :py:func:`keyset_diff`.
    If set to a list, it will be prefixed to every keypath in the
    output.

    '''
    if key is None:
        key = []

    options = {'common_key_threshold': common_key_threshold,
               'array_align': array_align,
               'compare_lengths': compare_lengths}

    common = (0.0 if common_key_threshold == 0.0 else
              commonality(left_struc, right_struc))
    if (common < common_key_threshold
        or not structure_comparable(left_struc, right_struc)):
        my_diff = this_level_diff(left_struc, right_struc, key, common)
    elif array_align:
        my_diff = needle_diff(left_struc, right_struc, key, options)
    else:
        my_diff = keyset_diff(left_struc, right_struc, key, options)

    if (compare_lengths and
        (len(compact_json_dumps([[key[:], copy.copy(right_struc)]])) <
         len(compact_json_dumps(my_diff)))):
        my_diff = [[key[:], copy.copy(right_struc)]]

    if key == []:
        my_diff = sort_stanzas(my_diff)
        if verbose:
            msg = ('Size of delta {:.3f}% size of original '
                   '(original: {} chars, delta: {} chars)')
            print(msg.format(*compute_diff_stats(right_struc, my_diff, True),
                  file=sys.stderr))
    return my_diff


def compute_diff_stats(target, diff, percent=True):
    '''Calculate the size of a minimal JSON dump of ``target`` and ``diff``,
    and the ratio of the two sizes.

    The ratio is expressed as a percentage if ``percent`` is ``True`` in
    a Boolean context , or as a float otherwise.

    Return value is a tuple of the form
    ``({ratio}, {size of target}, {size of diff})``

    >>> compute_diff_stats([{}, 'foo', 'bar'], [], False)
    (0.125, 16, 2)
    >>> compute_diff_stats([{}, 'foo', 'bar'], [[0], {}])
    (50.0, 16, 8)
    '''
    diff_size = float(len(compact_json_dumps(diff)))
    target_size = len(compact_json_dumps(target))
    fraction = diff_size / target_size
    if percent:
        fraction = fraction * 100
    return fraction, target_size, int(diff_size)


def needle_diff(left_struc, right_struc, key, options={}):
    '''Returns a diff between ``left_struc`` and ``right_struc``.

    If ``left_struc`` and ``right_struc`` are both serializable as
    arrays, this function will use a Needleman-Wunsch sequence
    alignment to find a minimal diff between them.  Otherwise, the
    inputs are passed on to :func:`keyset_diff`.

    This function probably shouldnâ€™t be called directly.  Instead, use
    :func:`diff`, which is mutually recursive with this function and
    :func:`keyset_diff` anyway.

    '''
    if type(left_struc) not in (list, tuple):
        return keyset_diff(left_struc, right_struc, key, options)
    assert type(right_struc) in (list, tuple)

    down_col = 0
    lastrow = [
        [[key + [sub_i]] for sub_i in range(i)]
        for i in range(len(left_struc), -1, -1)
    ]

    def modify_cand():
        '''Build the candidate diff that involves (potentially) modifying an
        element.'''
        if col_i + 1 < len(lastrow):
            basis = lastrow[col_i+1]
            mutand_key = key + [left_i]
            return (basis +
                    diff(left_elem, right_elem, key=mutand_key, **options))

    def delete_cand():
        '''Build the candidate diff that involves deleting an element.'''
        if row:
            basis = row[0]
            delend_key = key + [left_i]
            return (basis + [[delend_key]])

    def append_cand():
        '''Build the candidate diff that involves appending an element.'''
        if col_i == down_col:
            basis = lastrow[col_i]
            append_at_key = key + [append_key(lastrow[col_i], left_struc, key)]
            return (basis + [[append_at_key, right_elem]])

    def insert_cand():
        '''Build the candidate diff that involves an insert-and-shift.'''
        if col_i != down_col:
            basis = lastrow[col_i]
            # del_offset = len([s for s in basis if len(s) == 1])
            insertion_key = key + [right_i]
            return (basis + [[insertion_key, right_elem, 'i']])

    def estimate_udiff_length(diff):
        '''Estimate the length of a udiff based on ``diff``.'''
        out = 0
        for stanza in diff:
            try:
                key_matter = json.dumps(
                    follow_path(left_struc, stanza[0][len(key):]), indent=1
                )
                out += len(key_matter) + key_matter.count('\n')
            except (KeyError, IndexError):
                pass
            if len(stanza) > 1:
                assert 2 <= len(stanza) <= 3, stanza
                repl_matter = json.dumps(stanza[1], indent=1)
                out += len(repl_matter) + repl_matter.count('\n')
        return out

    for right_i, right_elem in enumerate(right_struc):
        # first_left_i = min(right_i, len(left_struc) - 1)
        # left_elems = left_struc[first_left_i:]
        col_i = len(left_struc)
        row = [insert_cand()]

        for left_i, left_elem in enumerate(left_struc):
            col_i = len(left_struc) - left_i - 1
            cands = (c for c in (modify_cand(), delete_cand(),
                                 append_cand(), insert_cand())
                     if c is not None)
            if options['array_align'] == 'udiff':
                winner = min(cands, key=estimate_udiff_length)
            else:
                winner = min(cands, key=lambda d: len(compact_json_dumps(d)))
            row.insert(0, winner)

        lastrow = row
    return winner


def append_key(stanzas, left_struc, keypath=()):
    '''Get the appropriate key for appending to the sequence ``left_struc``.

    ``stanzas`` should be a diff, some of whose stanzas may modify a
    sequence ``left_struc`` that appears at path ``keypath``.  If any of
    the stanzas append to ``left_struc``, the return value is the
    largest index in ``left_struc`` they address, plus one.
    Otherwise, the return value is ``len(left_struc)`` (i.e. the index
    that a value would have if it was appended to ``left_struc``).

    >>> append_key([], [])
    0
    >>> append_key([[[2], 'Baz']], ['Foo', 'Bar'])
    3
    >>> append_key([[[2], 'Baz'], [['Quux', 0], 'Foo']], [], ['Quux'])
    1

    '''
    keys = (s[0] for s in stanzas if s[0] == (list(keypath) + s[0][-1:]))
    addition_key = len(left_struc)
    for key in keys:
        addition_key = max(addition_key, key[-1] + 1)
    return addition_key


def compute_keysets(left_seq, right_seq):
    '''Compare the keys of ``left_seq`` vs. ``right_seq``.

    Determines which keys ``left_seq`` and ``right_seq`` have in
    common, and which are unique to each of the structures.  Arguments
    should be instances of the same basic type, which must be a
    non-terminal: i.e. :class:`list` or :class:`dict`.  If they are
    lists, the keys compared will be integer indices.

    Returns:
        Return value is a 3-tuple of sets ``({overlap}, {left_only},
        {right_only})``.  As their names suggest, ``overlap`` is a set
        of keys ``left_seq`` have in common, ``left_only`` represents
        keys only found in ``left_seq``, and ``right_only`` holds keys
        only found in ``right_seq``.

    Raises:
        AssertionError if ``left_seq`` is not an instance of
        ``type(right_seq)``, or if they are not of a non-terminal
        type.

    >>> (compute_keysets({'foo': None}, {'bar': None})
    ...  == (set([]), {'foo'}, {'bar'}))
    True
    >>> (compute_keysets({'foo': None, 'baz': None},
    ...                  {'bar': None, 'baz': None})
    ...  == ({'baz'}, {'foo'}, {'bar'}))
    True
    >>> (compute_keysets(['foo', 'baz'], ['bar', 'baz'])
    ...  == ({0, 1}, set([]), set([])))
    True
    >>> compute_keysets(['foo'], ['bar', 'baz']) == ({0}, set([]), {1})
    True
    >>> compute_keysets([], ['bar', 'baz']) == (set([]), set([]), {0, 1})
    True

    '''
    assert isinstance(left_seq, type(right_seq)), (left_seq, right_seq)
    assert type(left_seq) in NONTERMINALS, left_seq

    if type(left_seq) is dict:
        left_keyset = set(left_seq.keys())
        right_keyset = set(right_seq.keys())
    else:
        left_keyset = set(range(len(left_seq)))
        right_keyset = set(range(len(right_seq)))

    overlap = left_keyset.intersection(right_keyset)
    left_only = left_keyset - right_keyset
    right_only = right_keyset - left_keyset

    return (overlap, left_only, right_only)


def keyset_diff(left_struc, right_struc, key, options={}):
    '''Return a diff between ``left_struc`` and ``right_struc``.

    It is assumed that ``left_struc`` and ``right_struc`` are both
    non-terminal types (serializable as arrays or objects).  Sequences
    are treated just like mappings by this function, so the diffs will
    be correct but not necessarily minimal.  For a minimal diff
    between two sequences, use :func:`needle_diff`.

    This function probably shouldnâ€™t be called directly.  Instead, use
    :func:`diff`, which will call :func:`keyset_diff` if appropriate
    anyway.
    '''
    out = []
    (overlap, left_only, right_only) = compute_keysets(left_struc, right_struc)
    out.extend([[key + [k]] for k in left_only])
    out.extend([[key + [k], right_struc[k]] for k in right_only])
    for k in overlap:
        sub_key = key + [k]
        out.extend(diff(left_struc[k], right_struc[k],
                        key=sub_key, **options))
    return out


def this_level_diff(left_struc, right_struc, key=None, common=None):
    '''Return a sequence of diff stanzas between the structures
    ``left_struc`` and ``right_struc``, assuming that they are each at
    the key-path ``key`` within the overall structure.

    >>> (this_level_diff({'foo': 'bar', 'baz': 'quux'},
    ...                 {'foo': 'bar'})
    ...  == [[['baz']]])
    True
    >>> (this_level_diff({'foo': 'bar', 'baz': 'quux'},
    ...                 {'foo': 'bar'}, ['quordle'])
    ...  == [[['quordle', 'baz']]])
    True

    '''
    out = []

    if key is None:
        key = []

    if common is None:
        common = commonality(left_struc, right_struc)

    if common:
        (overlap, left, right) = compute_keysets(left_struc, right_struc)
        for okey in overlap:
            if left_struc[okey] != right_struc[okey]:
                out.append([key[:] + [okey], right_struc[okey]])
        for okey in left:
            out.append([key[:] + [okey]])
        for okey in right:
            out.append([key[:] + [okey], right_struc[okey]])
        return out
    elif left_struc != right_struc:
        return [[key[:], right_struc]]
    else:
        return []


def structure_comparable(left_struc, right_struc):
    '''Test if ``left_struc`` and ``right_struc`` can be efficiently diffed.'''
    if type(left_struc) is not type(right_struc):
        return False
    if type(left_struc) in TERMINALS:
        return False
    if len(left_struc) == 0 or len(right_struc) == 0:
        return False
    return True


def commonality(left_struc, right_struc):
    '''Return a float between ``0.0`` and ``1.0`` representing the amount
    that the structures ``left_struc`` and ``right_struc`` have in
    common.

    Return value is computed as the fraction (elements in common) /
    (total elements).

    '''

    if not structure_comparable(left_struc, right_struc):
        return 0.0

    if type(left_struc) is dict:
        (overlap, left, right) = compute_keysets(left_struc, right_struc)
        com = float(len(overlap))
        tot = len(overlap.union(left, right))
    else:
        assert type(left_struc) in (list, tuple), left_struc
        com = 0.0
        for elem in left_struc:
            if elem in right_struc:
                com += 1
        tot = max(len(left_struc), len(right_struc))

    return com / tot


def split_diff(stanzas):
    '''Split a diff into modifications, deletions and insertions.

    Return value is a 4-tuple of lists: the first is a list of stanzas
    from ``stanzas`` that modify JSON objects, the second is a list of
    stanzas that add or change elements in JSON arrays, the third is a
    list of stanzas which delete elements from arrays, and the fourth is
    a list of stanzas which insert elements into arrays (stanzas ending
    in ``"i"``).

    '''
    objs = [x for x in stanzas if in_object(x[0])]
    seqs = [x for x in stanzas if in_array(x[0])]
    assert len(objs) + len(seqs) == len(stanzas), stanzas
    seqs.sort(key=len)
    lengths = [len(x) for x in seqs]
    mod_point = bisect.bisect_left(lengths, 2)
    ins_point = bisect.bisect_left(lengths, 3)
    return (objs, seqs[mod_point:ins_point],
            seqs[:mod_point], seqs[ins_point:])


def sort_stanzas(stanzas):
    '''Sort the stanzas in a diff.

    Object changes can occur in any order, but deletions from arrays
    have to happen last node first: ``['foo', 'bar', 'baz']`` â†’
    ``['foo', 'bar']`` â†’ ``['foo']`` â†’ ``[]``; additions to arrays
    have to happen leftmost-node-first: ``[]`` â†’ ``['foo']`` â†’
    ``['foo', 'bar']`` â†’ ``['foo', 'bar', 'baz']``, and
    insert-and-shift alterations to arrays must happen last: ``['foo',
    'quux']`` â†’ ``['foo', 'bar', 'quux']`` â†’ ``['foo', 'bar', 'baz',
    'quux']``.

    Finally, stanzas are sorted in descending order of *length* of
    keypath, so that the most deeply-nested structures are altered
    before alterations which might change their keypaths take place.

    Note that this will also sort changes to objects (dicts)
    so that they occur first of all.

    '''
    if len(stanzas) <= 1:
        return stanzas
    # First we divide the stanzas using split_diff():
    (objs, mods, dels, ins) = split_diff(stanzas)
    # Then we sort modifications of lists in ascending order of keypath
    # (note that we canâ€™t tell appends from mods on the info available):
    mods.sort(key=lambda x: x[0])
    # Deletions from lists in descending order of keypath:
    dels.sort(key=lambda x: x[0], reverse=True)
    # And insert-and-shifts in ascending order of keypath:
    ins.sort(key=lambda x: x[0])
    # Finally, we sort by length of keypath:
    stanzas = (objs + mods + dels + ins)
    stanzas.sort(key=lambda s: len(s[0]), reverse=True)
    return stanzas
  0707010005c8c9000081b40000000000000000000000015ab217540000098a000000660000000000000000000000000000003000000000root/usr/share/opensvc/lib/json_delta/_patch.py   # -*- encoding: utf-8 -*-
# json_delta: a library for computing deltas between JSON-serializable
# structures.
# json_delta/_patch.py
#
# Copyright 2012â€’2015 Philip J. Roberts <himself@phil-roberts.name>.
# BSD License applies; see the LICENSE file, or
# http://opensource.org/licenses/BSD-2-Clause
'''Functions for applying JSON-format patches.'''
from __future__ import unicode_literals, print_function
import copy


def patch(struc, diff, in_place=True):
    '''Apply the sequence of diff stanzas ``diff`` to the structure
    ``struc``.

    By default, this function modifies ``struc`` in place; set
    ``in_place`` to ``False`` to return a patched copy of struc
    instead:

    >>> will_change = [16]
    >>> wont_change = [16]
    >>> patch(will_change, [[[0]]])
    []
    >>> will_change
    []
    >>> patch(wont_change, [[[0]]], False)
    []
    >>> wont_change
    [16]
    '''
    if not in_place:
        struc = copy.deepcopy(struc)
    for stanza in diff:
        struc = patch_stanza(struc, stanza)
    return struc


def patch_stanza(struc, stanza):
    '''Applies the stanza ``stanza`` to the structure ``struc`` as
    a patch.

    Note that this function modifies ``struc`` in-place into the
    target of ``stanza``.  If ``struc`` is a :func:`tuple`, you get a
    new tuple with the appropriate modification made:

    >>> patch_stanza((17, 3.141593, None), [[1], 3.14159265])
    (17, 3.14159265, None)

    '''
    changeback = False
    if type(struc) is tuple:
        changeback = True
        struc = list(struc)[:]
    key = stanza[0]
    if not key:
        struc = stanza[1]
        changeback = False
    elif len(key) == 1:
        if len(stanza) == 1:
            del struc[key[0]]
        elif len(stanza) == 3:
            assert stanza[2] == 'i', stanza
            if isinstance(struc, list):
                struc.insert(key[0], stanza[1])
            else:
                assert isinstance(struc, dict)
                struc[key[0]] = stanza[1]
        elif isinstance(struc, list) and key[0] == len(struc):
            struc.append(stanza[1])
        else:
            struc[key[0]] = stanza[1]
    else:
        pass_key = key[:]
        pass_struc_key = pass_key.pop(0)
        pass_struc = struc[pass_struc_key]
        pass_diff = [pass_key] + stanza[1:]
        struc[pass_struc_key] = patch_stanza(pass_struc, pass_diff)
    if changeback:
        struc = tuple(struc)
    return struc
  0707010005c8ca000081b40000000000000000000000015ab21754000049c8000000660000000000000000000000000000003000000000root/usr/share/opensvc/lib/json_delta/_udiff.py   # -*- encoding: utf-8 -*-
# json_delta: a library for computing deltas between JSON-serializable
# structures.
# json_delta/_udiff.py
#
# Copyright 2012â€’2015 Philip J. Roberts <himself@phil-roberts.name>.
# BSD License applies; see the LICENSE file, or
# http://opensource.org/licenses/BSD-2-Clause
'''Functions for computing udiffs.  Main entry point: :func:`udiff`.

The data structure representing a udiff that these functions all
manipulate is a pair of lists of iterators ``(left_lines,
right_lines)``.  These lists are expected (principally by
:func:`generate_udiff_lines()`, which processes them), to be of the
same length.  A pair of iterators ``(left_lines[i], right_lines[i])``
may yield exactly the same sequence of output lines, each with ``' '``
as the first character (representing parts of the structure the input
and output have in common).  Alternatively, they may each yield zero
or more lines (referring to parts of the structure that are unique to
the inputs they represent).  In this case, all lines yielded by
``left_lines[i]`` should begin with ``'-'``, and all lines yielded by
``right_lines[i]`` should begin with ``'+'``.



'''
from __future__ import print_function, unicode_literals
from ._util import TERMINALS, in_one_level, uniquify, Basestring
from ._util import predicate_count, zip, range
from ._diff import diff

from functools import partial
import copy
import json
import itertools

dumps = partial(json.dumps, ensure_ascii=False)


class Gap(object):
    '''Class to represent gaps introduced by sequence alignment.'''
    name = 'Gap'

    def __repr__(self):
        '''This docstring is mostly here to pacify pylint and achieve
        100% coverage for the test suite!

        >>> repr(Gap()) == '<Gap>'
        True
        '''
        return '<Gap>'


def udiff(left, right, patch=None, indent=0, use_ellipses=True, entry=True):
    '''Render the difference between the structures ``left`` and
    ``right`` as a string in a fashion inspired by :command:`diff -u`.

    Generating a udiff is strictly slower than generating a normal
    diff with the same option parameters, since the udiff is computed
    on the basis of a normal diff between ``left`` and ``right``.  If
    such a diff has already been computed (e.g. by calling
    :func:`diff`), pass it as the ``patch`` parameter:

    >>> (next(udiff({"foo": None}, {"foo": None}, patch=[])) ==
    ...  ' {...}')
    True

    As you can see above, structures that are identical in ``left`` and
    ``right`` are abbreviated using ``'...'`` by default.  To disable
    this behavior, set ``use_ellipses`` to ``False``.

    >>> ('\\n'.join(udiff({"foo": None}, {"foo": None},
    ...            patch=[], use_ellipses=False)) ==
    ... """ {
    ...  "foo":
    ...    null
    ...  }""")
    True

    >>> ('\\n'.join(udiff([None, None, None], [None, None, None],
    ...             patch=[], use_ellipses=False)) ==
    ... """ [
    ...   null,
    ...   null,
    ...   null
    ...  ]""")
    True

    '''
    if patch is None:
        patch = diff(left, right, array_align='udiff', compare_lengths=False,
                     common_key_threshold=0.0, verbose=False)
    elif entry:
        patch = copy.deepcopy(patch)

    if not patch and type(left) in TERMINALS:
        assert left == right, (left, right)
        if not entry:
            left_lines = [single_patch_band(indent, dumps(left))]
            right_lines = [single_patch_band(indent, dumps(left))]
        else:
            left_lines, right_lines = ([], [])
    elif patch and patch[0][0] == []:
        matter = dumps(left, indent=1).split('\n')
        left_lines = [patch_bands(indent, matter, '-')]
        if len(patch[0]) > 1:
            assert patch[0] == [[], right], (left, right, patch)
            matter = dumps(right, indent=1).split('\n')
            right_lines = [patch_bands(indent, matter, '+')]
    elif isinstance(left, dict):
        assert isinstance(right, dict), (left, right)
        left_lines, right_lines = udiff_dict(left, right, patch,
                                             indent, use_ellipses)
    else:
        assert isinstance(left, list), (left, right)
        assert isinstance(right, list), (left, right)
        left_lines, right_lines = udiff_list(left, right, patch,
                                             indent, use_ellipses)

    if entry:
        return generate_udiff_lines(left_lines, right_lines)
    else:
        return (left_lines, right_lines)


# The following functions represent functionality common to both
# udiff_dict() and udiff_list().  The forms without the leading _ are
# defined within the two functions, with the appropriate sequences for
# left_lines and right_lines curried in.


def _add_common_matter(matter, left_lines, right_lines, indent):
    '''Add the same matter to both left_lines and right_lines.'''
    for seq in left_lines, right_lines:
        add_matter(seq, matter, indent)


def _add_differing_matter(left, right, left_lines, right_lines, indent):
    '''Add left to left_lines, and right to right_lines.'''
    for matter, seq, sigil in ((left, left_lines, '-'),
                               (right, right_lines, '+')):
        if isinstance(matter, Basestring):
            matter = single_patch_band(indent, matter, sigil)
        add_matter(seq, matter, indent)


def _commafy_last(left_lines, right_lines, left_comma, right_comma):
    '''Add a comma to the last iterator in left_lines and right_lines, if
    left_comma and right_comma are True, respectively.'''
    if right_comma is None:
        right_comma = left_comma
    left_lines[-1] = commafy(left_lines[-1], left_comma)
    right_lines[-1] = commafy(right_lines[-1], right_comma)

# -----------------------------------------------------------------------------


def curry_functions(local_ns):
    '''Create partials of :func:`_add_common_matter`,
    :func:`_add_differing_matter` and :func:`_commafy_last`, with
    values for ``left_lines``, ``right_lines`` and (where appropriate)
    ``indent`` taken from the dictionary ``local_ns``.

    Appropriate defaults are also included in the partials, namely
    ``left=None`` and ``right=None`` for :func:`_add_differing_matter`
    and ``left_comma=True`` and ``right_comma=None`` for
    :func:`_commafy_last`.

    '''
    dict_subset = lambda keys: dict((k, local_ns[k]) for k in keys)
    acm_subset = dict_subset(('indent', 'left_lines', 'right_lines'))
    adm_subset = dict_subset(('indent', 'left_lines', 'right_lines'))
    adm_subset.update({'left': None, 'right': None})
    cl_subset = dict_subset(('left_lines', 'right_lines'))
    cl_subset.update({'left_comma': True, 'right_comma': None})
    return (partial(_add_common_matter, **acm_subset),
            partial(_add_differing_matter, **adm_subset),
            partial(_commafy_last, **cl_subset))


def udiff_dict(left, right, stanzas, indent=0, use_ellipses=True):
    '''Construct a human-readable delta between ``left`` and ``right``.

    This function probably shouldnâ€™t be called directly.  Instead, use
    :py:func:`udiff()` with the same arguments.  :py:func:`udiff()`
    and :py:func:`udiff_dict()` are mutually recursive, anyway.

    '''
    left_lines = []
    right_lines = []

    (add_common_matter,
     add_differing_matter,
     commafy_last) = curry_functions(vars())

    def dump_verbatim(obj, side='left', comma=False):
        '''Adds a verbatim JSON representation of obj to side.'''
        matter = dumps(obj, indent=1).split('\n')
        matter[0] = '{}: {}'.format(dumps(key), matter[0])
        gen = patch_bands(indent, matter, '-' if side == 'left' else '+')
        add_differing_matter(**{side: commafy(gen, comma)})

    if use_ellipses:
        keys_in_diff = uniquify([stanza[0][0] for stanza in stanzas])
    else:
        keys_in_diff = list(set(left.keys()).union(right.keys()))

    if not (left or right):
        add_common_matter('{}')
        return (left_lines, right_lines)
    if (not keys_in_diff) and use_ellipses:
        add_common_matter('{...}')
        return (left_lines, right_lines)

    add_common_matter('{')
    indent += 1

    common_keys = set(left.keys()).union(right.keys()).difference(keys_in_diff)
    if common_keys:
        key = next(iter(common_keys))
        value = next(udiff(left[key], right[key], [],
                           indent+1, entry=False)[0][0])
        line_matter = '{}: {}'.format(dumps(key), value.lstrip())
        add_common_matter(line_matter)
        if keys_in_diff or len(common_keys) > 1:
            commafy_last()
    if len(common_keys) > 1:
        left_comma = any(((key in left) for key in keys_in_diff))
        right_comma = any(((key in right) for key in keys_in_diff))
        assert left_comma or right_comma
        if left_comma and right_comma:
            add_common_matter('...,')
        elif left_comma:
            add_differing_matter(left='...,', right='...')
        else:
            add_differing_matter(left='...', right='...,')

    for i, key in enumerate(keys_in_diff):
        left_comma = any(((key in left) for key in keys_in_diff[i+1:]))
        right_comma = any(((key in right) for key in keys_in_diff[i+1:]))
        sub_diff = in_one_level(stanzas, key)
        if sub_diff == [[[]]]:  # Handle deletions from left
            dump_verbatim(left[key], 'left', left_comma)
        elif key not in left:  # Handle additions
            dump_verbatim(right[key], 'right', right_comma)
        else:  # Handle modifications
            [left_side, right_side] = udiff(
                left[key], right[key], sub_diff, indent+1,
                use_ellipses, entry=False
            )
            add_common_matter('{}:'.format(dumps(key)))
            add_differing_matter(left=left_side, right=right_side)
            commafy_last(left_comma=left_comma, right_comma=right_comma)
        if i != (len(keys_in_diff) - 1):
            add_common_matter('')

    indent -= 1
    add_common_matter('}')
    return left_lines, right_lines


def udiff_list(left, right, stanzas, indent=0, use_ellipses=True):
    '''Construct a human-readable delta between ``left`` and ``right``.

    This function probably shouldnâ€™t be called directly.  Instead, use
    :py:func:`udiff()` with the same arguments.  :py:func:`udiff()`
    and :py:func:`udiff_list()` are mutually recursive, anyway.

    '''
    left_lines = []
    right_lines = []

    (add_common_matter,
     add_differing_matter,
     commafy_last) = curry_functions(vars())

    def dump_verbatim(obj, side='left', comma=False):
        '''Adds a verbatim JSON representation of obj to side.'''
        matter = dumps(obj, indent=1).split('\n')
        gen = patch_bands(indent+1, matter, '-' if side == 'left' else '+')
        add_differing_matter(**{side: commafy(gen, comma)})

    if not (left or right):
        add_common_matter('[]')
        return (left_lines, right_lines)
    if not stanzas and use_ellipses:
        assert left == right, (left, right)
        material = '[...({})]'.format(len(right))
        add_common_matter(material)
        return (left_lines, right_lines)

    add_common_matter('[')
    left_a, right_a = reconstruct_alignment(left, right, stanzas)
    keys = (i for i in range(len(left_a)) if left_a[i] != right_a[i])

    def compute_commas(idx):
        '''Determine whether commas need to be appended to the patch bands
        representing the structures at left_a[idx] and right_a[idx].

        Output is a pair of bools.
        '''
        out = []
        for seq in left_a, right_a:
            out.append(any(((not isinstance(elem, Gap))
                            for elem in seq[idx:])))
        return tuple(out)

    pos = 0
    while pos < len(left_a):
        key = next(keys, len(left_a) - 1)
        assert key >= pos, (key, pos)
        sub_diff = in_one_level(
            stanzas, predicate_count(left_a[:key],
                                     lambda x: not isinstance(x, Gap))
        )
        pos_diff = key - pos

        if use_ellipses:
            if pos_diff >= 1:
                assert left_a[pos] == right_a[pos], (left_a, right_a, pos)
                left_ext, right_ext = udiff(
                    left_a[pos], right_a[pos], [],
                    indent+1, use_ellipses, entry=False
                )
                add_differing_matter(left=left_ext, right=right_ext)
            if pos_diff > 2:
                commafy_last()
                matter = '...({:d})'.format(pos_diff - 2)
                add_common_matter(matter, indent=indent+1)
            if pos_diff >= 2:
                assert left_a[key-1] == right_a[key-1], \
                    (left_a, right_a, key - 1)
                commafy_last()
                left_ext, right_ext = udiff(
                    left_a[key-1], right_a[key-1], [],
                    indent+1, use_ellipses, entry=False
                )
                add_differing_matter(left=left_ext, right=right_ext)
        else:
            for prepos in range(pos, key):
                left_ext, right_ext = udiff(
                    left_a[prepos], right_a[prepos],
                    in_one_level(stanzas, prepos),
                    indent+1, use_ellipses, entry=False
                )
                add_differing_matter(left=left_ext, right=right_ext)
                if (key - prepos > 1):
                    commafy_last()

        left_comma, right_comma = compute_commas(key)

        if left_comma and right_comma and pos_diff >= 1:
            commafy_last()
        elif left_comma and pos_diff >= 1:
            add_differing_matter(left=',')
        elif right_comma and pos_diff >= 1:
            add_differing_matter(right=',')

        left_comma, right_comma = compute_commas(key+1)

        if isinstance(right_a[key], Gap):
            dump_verbatim(left_a[key], 'left', left_comma)
        elif isinstance(left_a[key], Gap):
            dump_verbatim(right_a[key], 'right', right_comma)
        else:
            left_ext, right_ext = udiff(left_a[key], right_a[key], sub_diff,
                                        indent+1, use_ellipses, entry=False)
            add_differing_matter(left=left_ext, right=right_ext)
            if left_comma and right_comma:
                commafy_last()
            elif left_comma:
                add_differing_matter(left=',')
            elif right_comma:
                add_differing_matter(right=',')
        pos = key + 1

    add_common_matter(']')
    return (left_lines, right_lines)


def patch_bands(indent, material, sigil=' '):
    '''Generate appropriately indented patch bands, with ``sigil`` as
    the first character.'''
    indent = ' ' * indent
    for line in material:
        yield ''.join((sigil, indent, line))


def single_patch_band(indent, line, sigil=' '):
    '''Convenience function returning an iterable that generates a
    single patch band.'''
    return patch_bands(indent, (line,), sigil)


def commafy(gen, comma=True):
    '''Yield from ``gen``, ensuring that the final result ends with
    a comma iff ``comma`` is ``True``.

    >>> gen = ['Example line']
    >>> next(commafy(iter(gen))) == 'Example line,'
    True
    >>> next(commafy(iter(gen), False)) == 'Example line'
    True
    >>> gen = ['Line with a comma at the end,']
    >>> (next(commafy(iter(gen), comma=True))
    ...  == next(commafy(iter(gen), comma=False))
    ...  == 'Line with a comma at the end,')
    True
    '''
    comma = ',' if comma else ''
    prev = next(gen)
    for line in gen:
        yield prev
        prev = line
    if not prev.endswith(','):
        yield '{}{}'.format(prev, comma)
    else:
        yield prev


def add_matter(seq, matter, indent):
    '''Add material to ``seq``, treating it appropriately for its
    type.

    ``matter`` may be an iterator, in which case it is appended to
    ``seq``.  If it is a sequence, it is assumed to be a sequence of
    iterators, the sequence is concatenated onto ``seq``.  If
    ``matter`` is a string, it is turned into a patch band using
    :py:func:`single_patch_band`, which is appended.  Finally, if
    ``matter`` is ``None``, an empty iterable is appended to ``seq``.

    This function is a udiff-forming primitive, called by more
    specific functions defined within :py:func:`udiff_dict` and
    :py:func:`udiff_list`.

    '''
    if isinstance(matter, Basestring):
        seq.append(single_patch_band(indent, matter))
    elif matter is None:
        seq.append(iter(()))
    elif isinstance(matter, list) or isinstance(matter, tuple):
        seq.extend(matter)
    else:
        seq.append(matter)


def reconstruct_alignment(left, right, stanzas):
    '''Reconstruct the sequence alignment between the lists ``left``
    and ``right`` implied by ``stanzas``.'''
    indices = list(range(len(left)))

    for stanza in stanzas:
        if len(stanza[0]) == 1:
            key = stanza[0][0]
            if len(stanza) == 1:
                del indices[key]
            elif len(stanza) == 3:
                assert stanza[2] == 'i', stanza
                indices.insert(key, Gap())
            else:
                assert len(stanza) == 2, stanza

    left_a = []
    right_a = []
    origin = -1
    elems = iter(right)
    for (next_point, next_elem) in zip(indices, elems):
        if isinstance(next_point, Gap):
            left_a.append(next_point)
            right_a.append(next_elem)
        else:
            left_a.extend(left[origin+1:next_point+1])
            right_a.extend([Gap()] * (next_point - origin - 1))
            right_a.append(next_elem)
            origin = next_point
    left_a.extend(left[next_point+1:])
    right_a.extend(elems)

    short_a = min(left_a, right_a, key=len)
    short_a.extend([Gap()] * abs(len(left_a) - len(right_a)))
    assert len(left_a) == len(right_a), (left_a, right_a)
    return left_a, right_a


def generate_udiff_lines(left, right):
    '''Combine the diff lines from ``left`` and ``right``, and
    generate the lines of the resulting udiff.'''
    assert len(left) == len(right), (left, right)
    right_gens = iter(right)
    for left_g in left:
        right_g = next(right_gens)
        left_line = next(left_g, None)
        if (left_line is not None) and (left_line[0] == ' '):
            left_g = itertools.chain((left_line,), left_g)
            for line in left_g:
                right_line = next(right_g)
                assert line == right_line, (line, right_line)
                yield line
        else:
            if left_line is not None:
                assert left_line[0] == '-', left_line
                yield left_line
                for line in left_g:
                    assert line[0] == '-', line
                    yield line

            for line in right_g:
                assert line[0] == '+', line
                yield line
0707010005c8cb000081b40000000000000000000000015ab2175400001ef2000000660000000000000000000000000000003100000000root/usr/share/opensvc/lib/json_delta/_upatch.py  # -*- encoding: utf-8 -*-
from __future__ import print_function, unicode_literals
import json

from ._util import key_tracker, in_array, nearest_of
from ._diff import split_diff
from . import _patch


def upatch(struc, udiff, reverse=False, in_place=True):
    '''Apply a patch as output by :func:`json_delta.udiff()` to ``struc``.

    As with :func:`json_delta.patch`, ``struc`` is modified in place by
    default.  Set the parm ``in_place`` to ``False`` if this is not the
    desired behaviour.

    The udiff format has enough information in it that this
    transformation can be applied in reverse: i.e. if ``udiff`` is the
    output of ``udiff(left, right)``, you can reconstruct ``right``
    given ``left`` and ``udiff`` (by running ``upatch(left, udiff)``),
    or you can also reconstruct ``left`` given ``right`` and udiff (by
    running ``upatch(right, udiff, reverse=True)``).  This is not
    possible for JSON-format diffs, since a ``[keypath]`` stanza
    (meaning â€œdelete the structure at ``keypath``â€) does not record what
    the deleted structure was.

    '''
    diff = reconstruct_diff(udiff, reverse)
    return _patch.patch(struc, diff, in_place)


def ellipsis_handler(jstring, point, key):
    '''Extends :func:`key_tracker` to handle the ``â€¦`` construction.'''
    out_key = key[:]
    if jstring[point] == '.':
        if point+3 < len(jstring) and jstring[point:point+3] == '...':
            point += 3
            if in_array(key, accept_None=True) and jstring[point] == '(':
                increment = ''
                point += 1
                while point < len(jstring) and jstring[point] != ')':
                    assert jstring[point] in '0123456789'
                    increment += jstring[point]
                    point += 1
                out_key[-1] += int(increment) - 1
            point -= 1
        else:
            assert jstring[point-1] in '0123456789'
            assert jstring[point+1] in '0123456789'
    return (point, out_key)


def udiff_key_tracker(udiff, point=0, start_key=None):
    '''Find points within the udiff where the active keypath changes.'''
    for point, key in key_tracker(udiff, point, start_key, ellipsis_handler):
        yield point, key


def sort_stanzas(stanzas):
    '''Sorts the stanzas in a diff.

    :func:`reconstruct_diff` works on different assumptions from
    :func:`json_delta._diff.needle_diff` when it comes to stanzas
    altering arrays: keys in such stanzas relate to the elementâ€™s
    position within the arrayâ€™s longest intermediate representation
    during the transformation (that is after all insert-and-shifts,
    after all appends, but *before* any deletions).  This function sorts
    ``stanzas`` to reflect that order of operations.

    As with :func:`json_delta._diff.sort_stanzas` (which see), stanzas
    are sorted for length so the most deeply-nested structures get
    their modifications first.

    '''
    if len(stanzas) <= 1:
        return stanzas
    (objs, mods, dels, ins) = split_diff(stanzas)
    mods.sort(key=lambda x: x[0][-1])
    ins.sort(key=lambda x: x[0][-1])
    ins.sort(key=lambda x: len(x[0]))
    dels.sort(key=lambda x: x[0][-1], reverse=True)
    dels.sort(key=lambda x: len(x[0]), reverse=True)
    stanzas = ins + objs + mods + dels
    return stanzas


def is_none_key(key):
    '''Is the last element of ``key`` ``None``?'''
    return key and key[-1] is None


def skip_key(point, key, origin, keys, predicate):
    '''Find the next result in ``keys`` for which ``predicate(key)`` is ``False``.

    If none is found, or if ``key`` is already such a result, the
    return value is ``(point, key)``.

    '''
    while predicate(key):
        try:
            p, key = next(keys)
        except StopIteration:
            break
        point = origin + p
    return point, key


def reconstruct_diff(udiff, reverse=False):
    '''Turn a udiff back into a JSON-format diff.

    Set ``reverse`` to ``True`` to generate a reverse diff (i.e. swap
    the significance of line-initial ``+`` and ``-``).

    Header lines (if present) are ignored:

    >>> udiff = """--- <stdin>
    ... +++ <stdin>
    ... -false
    ... +true"""
    >>> reconstruct_diff(udiff)
    [[[], True]]
    >>> reconstruct_diff(udiff, reverse=True)
    [[[], False]]
    '''
    del_sigil = '+' if reverse else '-'
    add_sigil = '-' if reverse else '+'
    deletes = []
    diff = []
    del_key = []
    add_key = []
    point = 0
    # dec = JSONDecoder()

    def scrub_span(point, next_point, sigil):
        span = udiff[point:next_point-1]
        span = span.replace('\n{}'.format(sigil), '').strip('\r\n\t ')
        return span

    def gen_stanzas(point, max_point, adding=True):
        point += 1
        key = add_key if adding else del_key
        sigil = add_sigil if adding else del_sigil
        keys = udiff_key_tracker(udiff[point:max_point], 0, key)
        origin = point

        def build_output():
            span = scrub_span(point, next_point, sigil)
            if span and adding:
                return (next_key, [list(key), json.loads(span)])
            elif span:
                return (next_key, [list(key)])

        point, key = skip_key(point, key, origin, keys, is_none_key)
        next_key = top_key = key
        keys = ((p, k) for (p, k) in keys if len(k) == len(top_key))

        if not is_none_key(key):
            for p, next_key in keys:
                next_point, next_key = skip_key(
                    origin + p, next_key, origin, keys, lambda k: k == key
                )
                if next_key == tuple(key):
                    break

                out = build_output()
                if out is not None:
                    yield out

                point, key = skip_key(
                    next_point, next_key, origin, keys, is_none_key
                )

        next_point = max_point
        if not is_none_key(key):
            out = build_output()
            if out is not None:
                yield out

    if point + 3 <= len(udiff) and udiff[point:point+3] == '---':
        point = udiff[point:].find('\n') + point + 1
    if point + 3 <= len(udiff) and udiff[point:point+3] == '+++':
        point = udiff[point:].find('\n') + point + 1

    while point < len(udiff):
        if udiff[point] == ' ':
            if in_array(del_key, accept_None=True):
                assert in_array(add_key, accept_None=True)
                add_key = del_key = max(add_key, del_key)
            max_point = (nearest_of(udiff[point:], '\n{}'.format(del_sigil),
                                                   '\n{}'.format(add_sigil))
                         + point + 1)
            for p, del_key in udiff_key_tracker(udiff[point:max_point],
                                                0, del_key):
                pass
            for p, add_key in udiff_key_tracker(udiff[point:max_point],
                                                0, add_key):
                pass
        elif udiff[point] == del_sigil:
            max_point = (
                nearest_of(udiff[point:], '\n{}'.format(add_sigil), '\n ')
                + point + 1
            )
            for del_key, stanza in gen_stanzas(point, max_point, False):
                deletes.append(stanza)
        else:
            assert udiff[point] == add_sigil
            max_point = (
                nearest_of(udiff[point:], '\n{}'.format(del_sigil), '\n ')
                + point + 1
            )
            for add_key, stanza in gen_stanzas(point, max_point):
                diff.append(stanza)
        point = max_point
    keys_in_diff = [stanza[0] for stanza in diff]
    for stanza in diff:
        key = stanza[0]
        if (in_array(key, accept_None=True) and [key] not in deletes):
            stanza.append('i')
    diff.extend((d for d in deletes if d[0] not in keys_in_diff))
    return sort_stanzas(diff)
  0707010005c8cc000081b40000000000000000000000015ab217540000528c000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/json_delta/_util.py    # -*- coding: utf-8 -*-
# json_delta: a library for computing deltas between JSON-serializable
# structures.
# json_delta/_util.py
#
# Copyright 2012â€’2015 Philip J. Roberts <himself@phil-roberts.name>.
# BSD License applies; see the LICENSE file, or
# http://opensource.org/licenses/BSD-2-Clause
'''Utility functions and constants used by more than one submodule.

The majority of python 2/3 compatibility shims also appear in this
module.

'''

from __future__ import print_function, unicode_literals

import json
import codecs
import itertools

try:
    from itertools import izip as zip
except ImportError:
    zip = zip

from rcGlobalEnv import Storage

try:
    Basestring = basestring
except NameError:
    Basestring = str

try:
    IntegerTypes = (int, long)
except NameError:
    IntegerTypes = (int,)

TERMINALS = (str, int, float, bool, type(None))
try:
    TERMINALS += (unicode, long)
except NameError:
    pass
NONTERMINALS = (list, dict, Storage)
SERIALIZABLE_TYPES = TERMINALS + NONTERMINALS

ENCODINGS = ('utf_8', 'utf_16_le', 'utf_16_be', 'utf_32_le', 'utf_32_be')

try:
    range = xrange
except NameError:
    range = range


def licit_starts(start_chars='{}[]"-0123456789tfn \t\n\r'):
    '''Compute the bytestrings a UTF-x encoded string can begin with.

    This function is intended for encoding detection when the
    beginning of the encoded string must be one of a limited set of
    characters, as for JSON or the udiff format.  The argument
    ``start_chars`` must be an iterable of valid beginnings.

    '''
    bom_attr_name = lambda e: 'BOM_%s' % e.upper().replace('_', '', 1)
    out = dict((getattr(codecs, bom_attr_name(k)), k[:6]) for k in ENCODINGS)
    out[codecs.BOM_UTF8] = 'utf_8_sig'
    for encoding, start in itertools.product(ENCODINGS, start_chars):
        start = start.encode(encoding)
        assert start not in out, (start, out[start], encoding)
        out[start] = encoding
    return out

JSON_STARTS = licit_starts('{}[]"-0123456789tfn \t\n\r')
UDIFF_STARTS = licit_starts(' +-')


def sniff_encoding(bytestring, starts=JSON_STARTS, complete=True):
    r'''Determine the encoding of a UTF-x encoded string.

    The argument ``starts`` must be a mapping of bytestrings the input
    can begin with onto the encoding that such a beginning would
    represent (see :func:`licit_starts` for a function that can build
    such a mapping).

    The ``complete`` flag signifies whether the input represents the
    entire string: if it is set ``False``, the function will attempt
    to determine the encoding, but will raise a ``UnicodeError`` if it
    is ambiguous.  For example, an input of ``b'\xff\xfe'`` could be
    the UTF-16 little-endian byte-order mark, or, if the input is
    incomplete, it could be the first two characters of the UTF-32-LE
    BOM:

    >>> sniff_encoding(b'\xff\xfe') == 'utf_16'
    True
    >>> sniff_encoding(b'\xff\xfe', complete=False)
    Traceback (most recent call last):
        ...
    UnicodeError: String encoding is ambiguous.

    '''
    iteritems = getattr(starts, 'iteritems', starts.items)
    test = (lambda k, p: k == p if complete
            else lambda k, p: k.startswith(p))
    candidates = set()
    for limit in range(min(4, len(bytestring)), 0, -1):
        prefix = bytestring[:limit]
        candidates = set([v for k, v in iteritems() if test(k, prefix)])
        if len(candidates) == 1:
            return next(iter(candidates))
    if not candidates:
        raise ValueError('String does not begin with '
                         'any of the specified start chars.')
    else:
        raise UnicodeError('String encoding is ambiguous.')


def decode_json(file_or_str):
    '''Decode a JSON file-like object or string.

    The following doctest is probably pointless as documentation.  It is
    here so json-delta can claim 100% code coverage for its test suite!

    >>> try:
    ...     from StringIO import StringIO
    ... except ImportError:
    ...     from io import StringIO
    >>> foo = '[]'
    >>> decode_json(foo)
    []
    >>> decode_json(StringIO(foo))
    []
    '''
    if isinstance(file_or_str, bytes):
        file_or_str = file_or_str.decode(
            sniff_encoding(file_or_str, JSON_STARTS)
        )
    if isinstance(file_or_str, Basestring):
        return json.loads(file_or_str)
    return decode_json(read_bytestring(file_or_str))


def decode_udiff(file_or_str):
    r'''Decode a file-like object or bytestring udiff into a unicode string.

    The udiff may be encoded in UTF-8, -16 or -32 (with or without BOM):

    >>> udiff = u'- true\n+ false'
    >>> decode_udiff(udiff.encode('utf_32_be')) == udiff
    True
    >>> try:
    ...     from StringIO import StringIO
    ... except ImportError:
    ...     from io import BytesIO as StringIO
    >>> decode_udiff(StringIO(udiff.encode('utf-8-sig'))) == udiff
    True

    An empty string is a valid udiff; this function will convert it to
    a unicode string:

    >>> decode_udiff(b'') == u''
    True

    The function is idempotent: if you pass it a unicode string, it
    will be returned unmodified:

    >>> decode_udiff(udiff) is udiff
    True

    If you pass it a non-empty bytestring that cannot be interpreted
    as beginning with ``' '``, ``'+'``, ``'-'`` or a BOM in any
    encoding, a ``ValueError`` is raised:

    >>> decode_udiff(b':-)')
    Traceback (most recent call last):
        ...
    ValueError: String does not begin with any of the specified start chars.

    '''
    if isinstance(file_or_str, bytes):
        if not file_or_str:
            file_or_str = u''
        else:
            file_or_str = file_or_str.decode(
                sniff_encoding(file_or_str, UDIFF_STARTS)
            )
    if isinstance(file_or_str, Basestring):
        return file_or_str
    return decode_udiff(read_bytestring(file_or_str))


def read_bytestring(file):
    '''Read the contents of ``file`` as a :class:`bytes` object.'''
    file = getattr(file, 'buffer', file)
    bytestring = file.read()
    return bytestring


def predicate_count(iterable, predicate=lambda x: True):
    '''Count items ``x`` in ``iterable`` such that ``predicate(x)``.

    The default ``predicate`` is ``lambda x: True``, so
    ``predicate_count(iterable)`` will count the values generated by
    ``iterable``.  Note that if the iterable is a generator, this
    function will exhaust it, and if it is an infinite generator, this
    function will never return!

    >>> predicate_count([True] * 16)
    16
    >>> predicate_count([True, True, False, True, True], lambda x: x)
    4

    '''
    count = 0
    for item in iterable:
        if predicate(item):
            count += 1
    return count


def uniquify(obj, key=lambda x: x):
    '''Remove duplicate elements from a list while preserving order.

    ``key`` works as for :func:`min`, :func:`max`, etc. in the
    standard library.

    '''
    seen = set()
    seen_add = seen.add
    return [
        x for x in obj if (key(x) not in seen and not seen_add(key(x)))
    ]


def _load_and_func(func, parm1=None, parm2=None, both=None, **flags):
    '''Decode JSON-serialized parameters and apply func to them.'''
    if (parm1 is not None) and (parm2 is not None):
        return func(decode_json(parm1), decode_json(parm2), **flags)
    else:
        assert (both is not None), (parm1, parm2, both)
        [parm1, parm2] = decode_json(both)
        return func(parm1, parm2, **flags)


def stanzas_addressing(stanzas, keypath):
    '''Find diff stanzas modifying the structure at ``keypath``.

    The purpose of this function is to keep track of changes made to
    the overall structure by stanzas earlier in the sequence, e.g.:

    >>> struc = [
    ...     'foo',
    ...     'bar', [
    ...         'baz'
    ...     ]
    ... ]
    >>> stanzas = [
    ...     [ [2, 1], 'quux'],
    ...     [ [0] ],
    ...     [ [1, 2], 'quordle']
    ... ]
    >>> (stanzas_addressing(stanzas, [2])
    ...  == [
    ...   [ [1], 'quux' ],
    ...   [ [2], 'quordle' ]
    ...  ])
    True

    ``stanzas[0]`` and ``stanzas[2]`` both address the same element of
    ``struc`` â€” the list that starts off as ``['baz']``, even though
    their keypaths are completely different, because the diff stanza
    ``[[0]]`` moves the list ``['baz']`` from index 2 of ``struc`` to
    index 1.

    The return value is a sub-diff: a list of stanzas fit to modify
    the element at ``keypath`` within the overall structure.

    '''
    keypath = keypath[:]
    out = []
    for stanza in stanzas:
        stanza_keypath = stanza[0]
        if stanza_keypath[:len(keypath)] == keypath:
            out.append([stanza_keypath[len(keypath):]] + stanza[1:])
        elif (in_array(stanza_keypath)
              and len(stanza) in (1, 3)
              and len(stanza_keypath) <= len(keypath)):
            kp_mutand_pos = len(stanza_keypath) - 1
            assert (keypath[kp_mutand_pos] != stanza_keypath[-1]), \
                (stanza, keypath)
            if keypath[kp_mutand_pos] > stanza_keypath[-1]:
                if len(stanza) == 1:
                    keypath[kp_mutand_pos] -= 1
                else:
                    assert stanza == [stanza_keypath, stanza[1], 'i'], stanza
                    keypath[kp_mutand_pos] += 1
    return out


def in_one_level(stanzas, key):
    return stanzas_addressing(stanzas, [key])


def compact_json_dumps(obj):
    '''Compute the most compact possible JSON representation of ``obj``.

    >>> test = {
    ...             'foo': 'bar',
    ...             'baz':
    ...                ['quux', 'spam',
    ...       'eggs']
    ... }
    >>> compact_json_dumps(test) in (
    ...     '{"foo":"bar","baz":["quux","spam","eggs"]}',
    ...     '{"baz":["quux","spam","eggs"],"foo":"bar"}'
    ... )
    True
    >>>
    '''
    return json.dumps(obj, indent=None, separators=(',', ':'),
                      ensure_ascii=False)


def all_paths(struc):
    '''Generate key-paths to every node in ``struc``.

    Both terminal and non-terminal nodes are visited, like so:

    >>> paths = [x for x in all_paths({'foo': None, 'bar': ['baz', 'quux']})]
    >>> [] in paths # ([] is the path to ``struc`` itself.)
    True
    >>> ['foo'] in paths
    True
    >>> ['bar'] in paths
    True
    >>> ['bar', 0] in paths
    True
    >>> ['bar', 1] in paths
    True
    >>> len(paths)
    5
    '''
    yield []
    if isinstance(struc, dict):
        keys = struc.keys()
    elif isinstance(struc, list):
        keys = range(len(struc))
    else:
        return
    for key in keys:
        for subkey in all_paths(struc[key]):
            yield [key] + subkey


def follow_path(struc, path):
    '''Retrieve the value found at the key-path ``path`` within ``struc``.'''
    if not path:
        return struc
    else:
        return follow_path(struc[path[0]], path[1:])


def check_diff_structure(diff):
    '''Return ``diff`` (or ``True``) if it is structured as a sequence
    of ``diff`` stanzas.  Otherwise return ``False``.

    ``[]`` is a valid diff, so if it is passed to this function, the
    return value is ``True``, so that the return value is always true
    in a Boolean context if ``diff`` is valid.

    >>> check_diff_structure('This is certainly not a diff!')
    False
    >>> check_diff_structure([])
    True
    >>> check_diff_structure([None])
    False
    >>> example_valid_diff = [[["foo", 6, 12815316313, "bar"], None]]
    >>> check_diff_structure(example_valid_diff) == example_valid_diff
    True
    >>> check_diff_structure([[["foo", 6, 12815316313, "bar"], None],
    ...                       [["foo", False], True]])
    False
    '''
    if diff == []:
        return True
    if not isinstance(diff, list):
        return False
    for stanza in diff:
        conditions = (lambda s: isinstance(s, list),
                      lambda s: isinstance(s[0], list),
                      lambda s: len(s) in (1, 2, 3),
                      lambda s: len(s) != 3 or s[2] == 'i')
        for condition in conditions:
            if not condition(stanza):
                return False
        for key in stanza[0]:
            if not (type(key) in IntegerTypes or isinstance(key, Basestring)):
                # Checking type because isinstance(False, int) == True!
                return False
    return diff


def in_x_error(key, offender):
    '''Build the instance of ``ValueError`` :func:`in_object` and
    :func:`in_array` raise if ``keypath`` is invalid.'''
    msg = ('keypath elements must be instances of '
           'str, unicode, int or long,\n'
           '    not {} (key[{}] == {})'.format(
               type(offender).__name__, key.index(offender), repr(offender)
           ))
    return ValueError(msg)


def in_object(key, accept_None=False):
    '''Should the keypath ``key`` point at a JSON object (``{}``)?

    Works by testing whether ``key[-1]`` is a string or (where appropriate)
    :func:`unicode`:

    >>> in_object(["foo"])
    True
    >>> in_object([u'bar'])
    True

    Returns ``False`` if ``key`` addresses an arrayâ€¦

    >>> in_object([u'bar', 16])
    False
    >>> import sys
    >>> False if sys.version >= '3' else eval("in_object([u'bar', 16L])")
    False

    â€¦if ``key == []``â€¦

    >>> in_object([])
    False

    If the ``accept_None`` flag is set, this function will also return
    ``True`` if ``key[-1] is None`` (this functionality is used by
    :func:`key_tracker`, to signal points within a JSON string where a
    new object key is expected, but not yet found).

    >>> in_object([None])
    Traceback (most recent call last):
        ...
    ValueError: keypath elements must be instances of str, unicode, int or long,
        not NoneType (key[0] == None)

    >>> in_object([None], True)
    True
    >>> in_object([None], accept_None=True)
    True

    Raises a ``ValueError`` if ``key`` is not a valid keypath:

    >>> in_object(['foo', {}])
    Traceback (most recent call last):
        ...
    ValueError: keypath elements must be instances of str, unicode, int or long,
        not dict (key[1] == {})

    >>> in_object([False, u'foo'])
    Traceback (most recent call last):
        ...
    ValueError: keypath elements must be instances of str, unicode, int or long,
        not bool (key[0] == False)

    '''

    try:
        offender = next((k for k in key[:-1]
                         if (not isinstance(k, Basestring)
                             and type(k) not in IntegerTypes)))
    except StopIteration:
        pass
    else:
        raise in_x_error(key, offender)
    out = bool(key and (isinstance(key[-1], Basestring)
                        or (accept_None and key[-1] is None)))
    if not out and key and type(key[-1]) not in IntegerTypes:
        raise in_x_error(key, key[-1])
    return out


def in_array(key, accept_None=False):
    '''Should the keypath ``key`` point at a JSON array (``[]``)?

    Works by testing whether ``key[-1]`` is an :class:`int` or
    (where appropriate) :class:`long`:

    >>> in_array([u'bar', 16])
    True
    >>> import sys
    >>> sys.version >= '3' or eval("in_array([u'foo', 94L])")
    True

    Returns ``False`` if ``key`` addresses a non-array objectâ€¦

    >>> in_array(["foo"])
    False
    >>> in_array([u'bar'])
    False

    â€¦or if ``key == []`` (as in that case thereâ€™s no way of knowing
    whether ``key`` addresses an object or an array).

    >>> in_array([])
    False

    If the ``accept_None`` flag is set, this function will not raise a
    ``ValueError`` if ``key[-1] is None`` (keypaths of this form are
    used by :func:`key_tracker`, to signal points within a JSON string
    where a new object key is expected, but not yet found).

    >>> in_array([None])
    Traceback (most recent call last):
        ...
    ValueError: keypath elements must be instances of str, unicode, int or long,
        not NoneType (key[0] == None)

    >>> in_array([None], True)
    False
    >>> in_array([None], accept_None=True)
    False

    Otherwise, a ``ValueError`` is raised if ``key`` is not a valid keypath:

    >>> keypath = [{str("spam"): str("spam")}, "pickled eggs and spam", 7]
    >>> in_array(keypath)
    Traceback (most recent call last):
        ...
    ValueError: keypath elements must be instances of str, unicode, int or long,
        not dict (key[0] == {'spam': 'spam'})

    '''
    try:
        offender = next((k for k in key[:-1]
                         if (not isinstance(k, Basestring)
                             and type(k) not in IntegerTypes)))
    except StopIteration:
        pass
    else:
        raise in_x_error(key, offender)
    if accept_None and key and key[-1] is None:
        return False
    out = bool(key and type(key[-1]) in IntegerTypes)
    if not out and key and not isinstance(key[-1], Basestring):
        raise in_x_error(key, key[-1])
    return out


def nearest_of(string, *subs):
    '''Find the index of the substring in ``subs`` that occurs earliest in
    ``string``, or ``len(string)`` if none of them do.'''
    return min((string.find(x) if x in string else len(string) for x in subs))


def skip_string(jstring, point):
    r'''Assuming ``jstring`` is a string, and ``jstring[point]`` is a ``"`` that
    starts a JSON string, return ``x`` such that ``jstring[x-1]`` is
    the ``"`` that terminates the string.

    When a ``"`` is found, it is necessary to check that it is not
    escaped by a preceding backslash.  As a backslash may itself be
    escaped, this amounts to checking that the number of backslashes
    immediately preceding the ``"`` is even (counting 0 as an even
    number):

    >>> test_string = r'"Fred \"Foonly\" McQuux"'
    >>> skip_string(test_string, 0) == len(test_string)
    True
    >>> backslash = chr(0x5c)
    >>> dbl_quote = chr(0x22)
    >>> even_slashes = ((r'"\\\\\\"', json.dumps(backslash * 3)),
    ...                 (r'"\\\\"',   json.dumps(backslash * 2)),
    ...                 (r'"\\"',     json.dumps(backslash)))
    >>> all((json.loads(L) == json.loads(R) for (L, R) in even_slashes))
    True
    >>> all((skip_string(L, 0) == len(L) for (L, R) in even_slashes))
    True
    >>> def cat_dump(*args): return json.dumps(''.join(args))
    >>> odd_slashes = (
    ...     (r'"\\\\\\\"  "', cat_dump(backslash * 3, dbl_quote, ' ' * 2)),
    ...     (r'"\\\\\"    "', cat_dump(backslash * 2, dbl_quote, ' ' * 4)),
    ...     (r'"\\\"      "', cat_dump(backslash * 1, dbl_quote, ' ' * 6)),
    ...     (r'"\"        "', cat_dump(dbl_quote, ' ' * 8)),
    ... )
    >>> all((json.loads(L) == json.loads(R) for (L, R) in odd_slashes))
    True
    >>> all((skip_string(L, 0) == 12 for (L, R) in odd_slashes))
    True

    '''
    assert jstring[point] == '"'
    point += 1
    backslash_count = 0
    while not (jstring[point] == '"' and backslash_count % 2 == 0):
        if jstring[point] == '\\':
            backslash_count += 1
        else:
            backslash_count = 0
        point += 1
    return point + 1


def key_tracker(jstring, point=0, start_key=None, special_handler=None):
    '''Generate points within ``jstring`` where the keypath changes.

    This function also identifies points within objects where a new
    ``key: value`` pair is expected, by yielding a pseudo-keypath with
    ``None`` as the final element.

    Parameters:
        * ``jstring``: The JSON string to search.

        * ``point``: The point to start at.

        * ``start_key``: The starting keypath.

        * ``special_handler``: A function for handling extensions to
          JSON syntax (e.g. :py:func:`_upatch.ellipsis_handler`, used
          to handle the ``...`` construction in udiffs).

    >>> next(key_tracker('{}'))
    (1, (None,))
    '''
    if start_key is None:
        key = []
    else:
        key = list(start_key)

    while point < len(jstring):
        if jstring[point] == '{':
            key.append(None)
            yield (point + 1, tuple(key))
        elif jstring[point] == '[':
            key.append(0)
            yield (point + 1, tuple(key))
        elif jstring[point] in ']}':
            key.pop()
            yield (point + 1, tuple(key))
        elif jstring[point] == ',':
            if in_object(key, accept_None=True):
                key[-1] = None
            else:
                assert in_array(key, accept_None=True)
                key[-1] += 1
            yield (point + 1, tuple(key))
        elif jstring[point] == '"':
            string_end = skip_string(jstring, point)
            if (key and key[-1] is None):
                key[-1] = json.loads(jstring[point:string_end])
                while (string_end < len(jstring)
                       and jstring[string_end] in ' \r\n\t:'):
                    string_end += 1
                yield (string_end, tuple(key))
            point = string_end - 1
        elif special_handler is not None:
            point, newkey = special_handler(jstring, point, key)
            if key != newkey:
                key = newkey
                yield (point, tuple(key))
        point += 1
            
0707010005c8cd000081b40000000000000000000000015ab2175400003f57000000660000000000000000000000000000002700000000root/usr/share/opensvc/lib/keywords.py    """
The module implementing Keyword, Section and KeywordStore classes,
used to declared node and service configuration keywords and their
properties.
"""
from __future__ import print_function
import os
import copy
from textwrap import TextWrapper

from rcGlobalEnv import rcEnv

class MissKeyNoDefault(Exception):
    pass

class KeyInvalidValue(Exception):
    pass

class Keyword(object):
    def __init__(self, section, keyword,
                 rtype=None,
                 order=100,
                 required=False,
                 generic=False,
                 at=False,
                 inheritance="leaf > head",
                 scope_order="specific > generic",
                 default=None,
                 default_text=None,
                 candidates=None,
                 strict_candidates=True,
                 convert=None,
                 depends=[],
                 text="",
                 example="foo",
                 provisioning=False):
        self.section = section
        self.keyword = keyword
        if rtype is None or isinstance(rtype, list):
            self.rtype = rtype
        else:
            self.rtype = [rtype]
        self.order = order
        self.generic = generic
        self.at = at
        self.top = None
        self.required = required
        self.default = default
        self.default_text = default_text
        self.candidates = candidates
        self.strict_candidates = strict_candidates
        self.depends = depends
        self.text = text
        self.example = example
        self.provisioning = provisioning
        self.convert = convert
        self.inheritance = inheritance
        self.scope_order = scope_order

        if self.default_text is None:
            self.default_text = self.default

    def __lt__(self, o):
        return self.order < o.order

    def __getattribute__(self, attr):
        if attr == "default":
            return copy.copy(object.__getattribute__(self, attr))
        return object.__getattribute__(self, attr)

    def deprecated(self):
        if self.keyword in self.top.deprecated_keywords:
            return True
        if self.rtype is None:
            return self.section+"."+self.keyword in self.top.deprecated_keywords
        for rtype in self.rtype:
            if self.section+"."+rtype+"."+self.keyword in self.top.deprecated_keywords:
                return True
        return False

    def template(self, fmt="text", section=None):
        if self.deprecated():
            return ''

        if fmt == "text":
            return self.template_text()
        elif fmt == "rst":
            return self.template_rst(section=section)
        else:
            return ""

    def template_text(self):
        wrapper = TextWrapper(subsequent_indent="#%18s"%"", width=78)

        depends = " && ".join(["%s in %s"%(d[0], d[1]) for d in self.depends])
        if depends == "":
            depends = None

        if isinstance(self.candidates, (list, tuple, set)):
            candidates = " | ".join([str(x) for x in self.candidates])
        else:
            candidates = str(self.candidates)
        if not self.strict_candidates:
            candidates += " ..."

        s = '#\n'
        s += "# keyword:          %s\n"%self.keyword
        s += "# ----------------------------------------------------------------------------\n"
        s += "#  scopable:        %s\n"%str(self.at)
        s += "#  required:        %s\n"%str(self.required)
        if self.top.has_default_section:
            s += "#  provisioning:    %s\n"%str(self.provisioning)
        s += "#  default:         %s\n"%str(self.default_text)
        if self.top.has_default_section:
            s += "#  inheritance:     %s\n"%str(self.inheritance)
        s += "#  scope order:     %s\n"%str(self.scope_order)
        if self.candidates:
            s += "#  candidates:      %s\n"%candidates
        if depends:
            s += "#  depends:         %s\n"%depends
        if self.convert:
            s += "#  convert:         %s\n"%str(self.convert)
        s += '#\n'
        if self.text:
            wrapper = TextWrapper(subsequent_indent="#%9s"%"", width=78)
            s += wrapper.fill("#  desc:  "+self.text) + "\n"
        s += '#\n'
        if self.default_text is not None:
            val = self.default_text
        elif self.candidates and len(self.candidates) > 0:
            val = self.candidates[0]
        else:
            val = self.example
        s += ";" + self.keyword + " = " + str(val) + "\n\n"
        return s

    def template_rst(self, section=None):
        depends = " && ".join(["%s in %s"%(d[0], d[1]) for d in self.depends])
        if depends == "":
            depends = None

        if isinstance(self.candidates, (list, tuple, set)):
            candidates = " | ".join([str(x) for x in self.candidates])
        else:
            candidates = str(self.candidates)
        if not self.strict_candidates:
            candidates += " ..."

        s = ""
        if section:
            s += ".. _%s.%s:\n\n" % (section, self.keyword)

        s += ':kw:`%s`\n' % self.keyword
        s += "=" * (len(self.keyword) + 6) + "\n"
        s += "\n"
        s += "================= ================================================================\n"
        s += "**scopable**      %s\n"%str(self.at)
        s += "**required**      %s\n"%str(self.required)
        if self.top.has_default_section:
            s += "**provisioning**  %s\n"%str(self.provisioning)
        s += "**default**       %s\n"%str(self.default_text)
        if self.top.has_default_section:
            s += "**inheritance**   %s\n"%str(self.inheritance)
        s += "**scope order**   %s\n"%str(self.scope_order)
        if self.candidates:
            s += "**candidates**    %s\n"%candidates
        if depends:
            s += "**depends**       %s\n"%depends
        if self.convert:
            s += "**convert**       %s\n"%str(self.convert)
        s += "================= ================================================================\n"
        s += '\n'
        if self.text:
            s += self.text + "\n"
        s += '\n'
        return s

class Section(object):
    def __init__(self, section, top=None):
        self.section = section
        self.top = top
        self.keywords = []

    def __iadd__(self, o):
        if not isinstance(o, Keyword):
            return self
        self.keywords.append(o)
        return self

    def template(self, fmt="text"):
        k = self.getkey("type")
        if k is None:
            return self._template(fmt=fmt)
        if k.candidates is None:
            return self._template(fmt=fmt)
        s = ""
        if not k.strict_candidates:
            s += self._template(fmt=fmt)
        for t in k.candidates:
            s += self._template(t, fmt=fmt)
        return s

    def _template(self, rtype=None, fmt="text"):
        section = self.section
        if self.section in self.top.deprecated_sections:
            return ""
        if rtype and self.section+"."+rtype in self.top.deprecated_sections:
            return ""
        if fmt == "text":
            return self._template_text(rtype, section)
        elif fmt == "rst":
            return self._template_rst(rtype, section)
        else:
            return ""

    def _template_text(self, rtype, section):
        dpath = rcEnv.paths.pathdoc
        fpath = os.path.join(dpath, self.top.template_prefix+section+".conf")
        if rtype:
            section += ", type "+rtype
            fpath = os.path.join(dpath, self.top.template_prefix+self.section+"."+rtype+".conf")
        s = "#"*78 + "\n"
        s += "# %-74s #\n" % " "
        s += "# %-74s #\n" % section
        s += "# %-74s #\n" % " "
        s += "#"*78 + "\n\n"
        if section in self.top.base_sections:
            s += "[%s]\n" % self.section
        else:
            s += "[%s#0]\n" % self.section
        if rtype is not None:
            s += ";type = " + rtype + "\n\n"
        for keyword in sorted(self.getkeys(rtype)):
            s += keyword.template(fmt="text")
        for keyword in sorted(self.getprovkeys(rtype)):
            s += keyword.template(fmt="text")
        if rtype is not None:
            for keyword in sorted(self.getkeys()):
                if keyword.keyword == "type":
                    continue
                s += keyword.template(fmt="text")
        with open(fpath, "w") as f:
            f.write(s)
        return s

    def _template_rst(self, rtype, section):
        dpath = os.path.join(rcEnv.paths.pathtmp, "rst")
        if not os.path.exists(dpath):
            os.makedirs(dpath)
        if rtype:
            section += "."+rtype
            fpath = os.path.join(dpath, self.top.template_prefix+self.section+"."+rtype+".rst")
        else:
            fpath = os.path.join(dpath, self.top.template_prefix+section+".rst")
        s = section + "\n"
        s += "*" * len(section) + "\n\n"
        if self.top.template_prefix != "template.node." and len(section.split('.')) > 1:
            s += ".. include:: template.service." + section + ".example\n\n"
        for keyword in sorted(self.getkeys(rtype)):
            s += keyword.template(fmt="rst", section=section)
        for keyword in sorted(self.getprovkeys(rtype)):
            s += keyword.template(fmt="rst", section=section)
        if rtype is not None:
            for keyword in sorted(self.getkeys()):
                if keyword.keyword == "type":
                    continue
                s += keyword.template(fmt="rst", section=section)
        with open(fpath, "w") as f:
            f.write(s)
        return s

    def getkeys(self, rtype=None):
        if rtype is None:
            return [k for k in self.keywords if k.rtype is None and not k.provisioning]
        else:
            return [k for k in self.keywords if k.rtype and rtype in k.rtype and not k.provisioning]

    def getprovkeys(self, rtype=None):
        if rtype is None:
            return [k for k in self.keywords if k.rtype is None and k.provisioning]
        else:
            return [k for k in self.keywords if k.rtype and rtype in k.rtype and k.provisioning]

    def getkey(self, keyword, rtype=None):
        if '@' in keyword:
            l = keyword.split('@')
            if len(l) != 2:
                return
            keyword, _ = l
        if rtype:
            fkey = ".".join((self.section, rtype, keyword))
            if fkey in self.top.deprecated_keywords:
                keyword = self.top.deprecated_keywords[fkey]
                if keyword is None:
                    return
            for k in self.keywords:
                if k.keyword != keyword:
                    continue
                if k.rtype is None:
                    return k
                elif isinstance(k.rtype, list) and rtype in k.rtype:
                    return k
                elif rtype == k.rtype:
                    return k
        else:
            fkey = ".".join((self.section, keyword))
            if fkey in self.top.deprecated_keywords:
                keyword = self.top.deprecated_keywords[fkey]
            for k in self.keywords:
                if k.keyword == keyword:
                    return k
        return

class KeywordStore(dict):
    def __init__(self, provision=False, deprecated_keywords={}, deprecated_sections={},
                 template_prefix="template.", base_sections=[], has_default_section=True):
        dict.__init__(self)
        self.sections = {}
        self.deprecated_sections = deprecated_sections
        self.deprecated_keywords = deprecated_keywords
        self.template_prefix = template_prefix
        self.base_sections = base_sections
        self.provision = provision
        self.has_default_section = has_default_section

    def __iadd__(self, o):
        if not isinstance(o, Keyword):
            return self
        o.top = self
        if o.section not in self.sections:
            self.sections[o.section] = Section(o.section, top=self)
        self.sections[o.section] += o
        return self

    def __getattr__(self, key):
        return self.sections[str(key)]

    def __getitem__(self, key):
        k = str(key)
        if k not in self.sections:
            return Section(k)
        return self.sections[str(key)]

    def print_templates(self, fmt="text"):
        """
        Print templates in the spectified format (text by default, or rst).
        """
        for section in sorted(self.sections):
            print(self.sections[section].template(fmt=fmt))

    def required_keys(self, section, rtype=None):
        """
        Return the list of required keywords in the section for the resource
        type specified by <rtype>.
        """
        if section not in self.sections:
            return []
        return [k for k in sorted(self.sections[section].getkeys(rtype)) if k.required is True]

    def purge_keywords_from_dict(self, d, section):
        """
        Remove unknown keywords from a section.
        """
        if section == "env":
            return d
        if 'type' in d:
            rtype = d['type']
        else:
            rtype = None
        delete_keywords = []
        for keyword in d:
            key = self.sections[section].getkey(keyword)
            if key is None and rtype is not None:
                key = self.sections[section].getkey(keyword, rtype)
            if key is None:
                if keyword != "rtype":
                    print("Remove unknown keyword '%s' from section '%s'"%(keyword, section))
                    delete_keywords.append(keyword)

        for keyword in delete_keywords:
            del d[keyword]

        return d

    def update(self, rid, d):
        """
        Given a resource dictionary, spot missing required keys
        and provide a new dictionary to merge populated by default
        values.
        """
        import copy
        completion = copy.copy(d)

        # decompose rid into section and rtype
        if rid in ('DEFAULT', 'env'):
            section = rid
            rtype = None
        else:
            if '#' not in rid:
                return {}
            l = rid.split('#')
            if len(l) != 2:
                return {}
            section = l[0]
            if 'type' in d:
                rtype = d['type']
            elif self[section].getkey('type') is not None and \
                 self[section].getkey('type').default is not None:
                rtype = self[section].getkey('type').default
            else:
                rtype = None

        # validate command line dictionary
        for keyword, value in d.items():
            if section == "env":
                break
            if section not in self.sections:
                raise KeyInvalidValue("'%s' driver family is not valid in section '%s'"%(section, rid))
            key = self.sections[section].getkey(keyword)
            if key is None and rtype is not None:
                key = self.sections[section].getkey(keyword, rtype)
            if key is None:
                continue
            if key.strict_candidates and key.candidates is not None and value not in key.candidates:
                raise KeyInvalidValue("'%s' keyword has invalid value '%s' in section '%s'"%(keyword, str(value), rid))

        # add missing required keys if they have a known default value
        for key in self.required_keys(section, rtype):
            fkey = ".".join((section, str(rtype), key.keyword))
            if fkey in self.deprecated_keywords:
                continue

            if key.keyword in d:
                continue
            if key.keyword in [x.split('@')[0] for x in d]:
                continue
            if key.default is None:
                raise MissKeyNoDefault("No default value for required key '%s' in section '%s'"%(key.keyword, rid))
            print("Implicitely add [%s] %s = %s" % (rid, key.keyword, str(key.default)))
            completion[key.keyword] = key.default

        # purge unknown keywords and provisioning keywords
        completion = self.purge_keywords_from_dict(completion, section)

        return completion
 0707010005c8ce000081b40000000000000000000000015ab217540000195d000000660000000000000000000000000000002300000000root/usr/share/opensvc/lib/lock.py    """
Implement a node-wide locking mecanism.
File-based, lock with fnctl exclusive open when available.
"""

from __future__ import print_function
import os
import sys
import time
import json
import contextlib

from rcGlobalEnv import rcEnv

class LockNoLockFile(Exception):
    """ no lockfile specified
    """

class LockCreateError(Exception):
    """ could not create lockfile
    """

class LockAcquire(Exception):
    """ could not acquire lock on lockfile
    """
    def __init__(self, intent="", pid=0, progress=None):
        Exception.__init__(self)
        self.intent = intent
        self.pid = pid
        self.progress = progress
    def __str__(self):
        s = "holder pid %(pid)d, holder intent '%(intent)s'" % dict(pid=self.pid, intent=self.intent)
        if self.progress:
            s += ", progress '%s'" % str(self.progress)
        return s

class LockTimeout(LockAcquire):
    """ acquire lock timed out
    """

LOCK_EXCEPTIONS = (
    LockTimeout,
    LockNoLockFile,
    LockCreateError,
    LockAcquire,
)

def bencode(buff):
    """
    Try a bytes cast, which only work in python3.
    """
    try:
        return bytes(buff, "utf-8")
    except TypeError:
        return buff

def bdecode(buff):
    """
    On python, convert bytes to string using utf-8 and ascii as a fallback
    """
    if buff is None:
        return buff
    if sys.version_info[0] < 3:
        return buff
    if type(buff) == str:
        return buff
    else:
        try:
            return str(buff, "utf-8")
        except:
            return str(buff, "ascii")
    return buff

@contextlib.contextmanager
def cmlock(*args, **kwargs):
    """
    A context manager protecting a code path that can't run twice on the
    same node.
    """
    lockfd = None
    try:
        lockfd = lock(*args, **kwargs)
        yield lockfd
    finally:
        unlock(lockfd)

def lock(timeout=30, delay=1, lockfile=None, intent=None):
    """
    The lock acquire function.
    """
    if timeout == 0 or delay == 0:
        ticks = [0]
    else:
        ticks = range(int(float(timeout)/float(delay)))
    if len(ticks) == 0:
        ticks = [0]
    err = {}
    for tick in ticks:
        if tick > 0:
            time.sleep(delay)
        try:
            return lock_nowait(lockfile, intent)
        except LockAcquire as exc:
            err["intent"] = exc.intent
            err["pid"] = exc.pid
        except Exception:
            raise
    raise LockTimeout(**err)

def lock_nowait(lockfile=None, intent=None):
    """
    A lock acquire function variant without timeout not delay.
    """
    if lockfile is None:
        raise LockNoLockFile

    data = {"pid": os.getpid(), "intent": intent}
    lockd = os.path.dirname(lockfile)

    if not os.path.exists(lockd):
        os.makedirs(lockd)

    try:
        with open(lockfile, 'r') as ofile:
            prev_data = json.load(ofile)
        if not isinstance(prev_data, dict) or "pid" not in prev_data or "intent" not in prev_data:
            prev_data = {"pid": 0, "intent": ""}
    except Exception:
        prev_data = {"pid": 0, "intent": ""}

    # test if we already own the lock
    if prev_data["pid"] == os.getpid():
        return

    if os.path.isdir(lockfile):
        raise LockCreateError("lockfile points to a directory")

    try:
        flags = os.O_RDWR|os.O_CREAT
        if rcEnv.sysname != 'Windows':
            flags |= os.O_SYNC
        lockfd = os.open(lockfile, flags, 0o644)
    except Exception as exc:
        raise LockCreateError(str(exc))

    try:
        # FD_CLOEXEC makes sure the lock is the held by processes
        # we fork from this process
        if os.name == 'posix':
            import fcntl
            fcntl.flock(lockfd, fcntl.LOCK_EX|fcntl.LOCK_NB)
            flags = fcntl.fcntl(lockfd, fcntl.F_GETFD)
            flags |= fcntl.FD_CLOEXEC

            # acquire lock
            fcntl.fcntl(lockfd, fcntl.F_SETFD, flags)
        elif os.name == 'nt':
            import msvcrt
            size = os.path.getsize(lockfile)
            msvcrt.locking(lockfd, msvcrt.LK_RLCK, size)

        # drop our pid and intent in the lockfile, best effort
        try:
            os.ftruncate(lockfd, 0)
            os.write(lockfd, bencode(json.dumps(data)))
            os.fsync(lockfd)
        except Exception:
            pass
        return lockfd
    except IOError:
        os.close(lockfd)
        raise LockAcquire(**prev_data)
    except:
        os.close(lockfd)
        raise

def unlock(lockfd):
    """
    The lock release function.
    """
    if lockfd is None:
        return
    try:
        os.close(lockfd)
    except Exception:
        # already released by a parent process ?
        pass

def progress(lockfd, data):
    if lockfd is None:
        return
    try:
        _lockfd = os.dup(lockfd)
        with os.fdopen(_lockfd, "w+") as ofile:
            ofile.seek(0)
            try:
                lock_data = json.load(ofile)
            except ValueError as exc:
                return
            lock_data["progress"] = data
            ofile.truncate(0)
            ofile.seek(0)
            json.dump(lock_data, ofile)
            os.fsync(_lockfd)
        os.close(_lockfd)
    except Exception as exc:
        return

def main():
    """
    Expose the locking functions as a command line tool.
    """
    import optparse

    parser = optparse.OptionParser()
    parser.add_option("-f", "--file", default="/tmp/test.lock", action="store",
                      dest="file", help="The file to lock")
    parser.add_option("-i", "--intent", default="test", action="store",
                      dest="intent", help="The lock intent")
    parser.add_option("-t", "--time", default=60, action="store", type="int",
                      dest="time", help="The time we will hold the lock")
    parser.add_option("--timeout", default=1, action="store", type="int",
                      dest="timeout",
                      help="The time before failing to acquire the lock")
    (options, _) = parser.parse_args()
    try:
        with cmlock(timeout=options.timeout, delay=1, lockfile=options.file,
                    intent=options.intent):
            print("lock acquired")
            try:
                time.sleep(options.time)
            except KeyboardInterrupt:
                pass
    except Exception as exc:
        print(exc, file=sys.stderr)
        return 1

if __name__ == "__main__":
    import sys
    sys.exit(main())
   0707010005c8cf000081b40000000000000000000000015ab2175400024a58000000660000000000000000000000000000002300000000root/usr/share/opensvc/lib/node.py    # -*- coding: utf8 -*-

"""
This module implements the Node class.
The node
* handles communications with the collector
* holds the list of services
* has a scheduler
"""
from __future__ import print_function
from __future__ import absolute_import
from __future__ import division

import os
import datetime
import sys
import json
import socket
import time
import re

if sys.version_info[0] < 3:
    from urllib2 import Request, urlopen
    from urllib2 import HTTPError
    from urllib import urlencode
else:
    from urllib.request import Request, urlopen
    from urllib.error import HTTPError
    from urllib.parse import urlencode

import svcBuilder
import xmlrpcClient
from rcGlobalEnv import rcEnv, Storage
import rcLogger
import rcExceptions as ex
import rcConfigParser
from freezer import Freezer
from rcScheduler import Scheduler, SchedOpts, sched_action
from rcColor import formatter
from rcUtilities import justcall, lazy, lazy_initialized, vcall, check_privs, \
                        call, which, purge_cache_expired, read_cf, unset_lazy, \
                        drop_option, is_string, try_decode
from converters import *
from comm import Crypt
from extconfig import ExtConfig

if sys.version_info[0] < 3:
    BrokenPipeError = IOError

os.environ['LANG'] = 'C'

DEFAULT_STATUS_GROUPS = [
    "hb",
    "arbitrator",
]

REMOTE_ACTIONS = [
    "freeze",
    "reboot",
    "shutdown",
    "thaw",
    "updatepkg",
]

ACTIONS_NO_PARALLEL = [
    "edit_config",
    "get",
    "print_config",
    "print_resource_status",
    "print_schedule",
    "print_status",
]

ACTIONS_NO_MULTIPLE_SERVICES = [
    "print_resource_status",
]

CONFIG_DEFAULTS = {
    "node_env": "TST",
    "push_schedule": "00:00-06:00",
    "sync_schedule": "04:00-06:00",
    "comp_schedule": "02:00-06:00",
    "collect_stats_schedule": "@10",
    "no_schedule": "",
}

UNPRIVILEGED_ACTIONS = [
    "collector_cli",
]

class Node(Crypt, ExtConfig):
    """
    Defines a cluster node.  It contain list of Svc.
    Implements node-level actions and checks.
    """
    # for ExtConfig
    default_status_groups = DEFAULT_STATUS_GROUPS

    def __str__(self):
        return self.nodename

    def __init__(self):
        self.config = None
        self.auth_config = None
        self.clouds = None
        self.paths = Storage(
            reboot_flag=os.path.join(rcEnv.paths.pathvar, "REBOOT_FLAG"),
            tmp_cf=os.path.join(rcEnv.paths.pathvar, "node.conf.tmp"),
            cf=rcEnv.paths.nodeconf,
        )
        self.services = None
        self.load_config()
        self.options = Storage(
            cron=False,
            syncrpc=False,
            force=False,
            debug=False,
            stats_dir=None,
            begin=None,
            end=None,
            moduleset="",
            module="",
            node=None,
            ruleset_date="",
            objects=[],
            format=None,
            user=None,
            api=None,
            resource=[],
            mac=None,
            broadcast=None,
            param=None,
            value=None,
            extra_argv=[],
        )
        self.set_collector_env()
        self.log = rcLogger.initLogger(rcEnv.nodename)

    @lazy
    def kwdict(self):
        return __import__("nodedict")

    @lazy
    def devnull(self):
        return os.open(os.devnull, os.O_RDWR)

    @lazy
    def var_d(self):
        var_d = os.path.join(rcEnv.paths.pathvar, "node")
        if not os.path.exists(var_d):
            os.makedirs(var_d, 0o755)
        return var_d

    @property
    def svcs(self):
        if self.services is None:
            return None
        return list(self.services.values())

    @lazy
    def freezer(self):
        """
        Lazy allocator for the freezer object.
        """
        return Freezer("node")

    @lazy
    def sched(self):
        """
        Lazy initialization of the node Scheduler object.
        """
        return Scheduler(
            config_defaults=CONFIG_DEFAULTS,
            options=self.options,
            config=self.config,
            log=self.log,
            scheduler_actions={
                "checks": SchedOpts(
                    "checks"
                ),
                "dequeue_actions": SchedOpts(
                    "dequeue_actions",
                    schedule_option="no_schedule"
                ),
                "pushstats": SchedOpts(
                    "stats"
                ),
                "collect_stats": SchedOpts(
                    "stats_collection",
                    schedule_option="collect_stats_schedule"
                ),
                "pushpkg": SchedOpts(
                    "packages"
                ),
                "pushpatch": SchedOpts(
                    "patches"
                ),
                "pushasset": SchedOpts(
                    "asset"
                ),
                "pushnsr": SchedOpts(
                    "nsr",
                    schedule_option="no_schedule"
                ),
                "pushhp3par": SchedOpts(
                    "hp3par",
                    schedule_option="no_schedule"
                ),
                "pushemcvnx": SchedOpts(
                    "emcvnx",
                    schedule_option="no_schedule"
                ),
                "pushcentera": SchedOpts(
                    "centera",
                    schedule_option="no_schedule"
                ),
                "pushnetapp": SchedOpts(
                    "netapp",
                    schedule_option="no_schedule"
                ),
                "pushibmds": SchedOpts(
                    "ibmds",
                    schedule_option="no_schedule"
                ),
                "pushdcs": SchedOpts(
                    "dcs",
                    schedule_option="no_schedule"
                ),
                "pushfreenas": SchedOpts(
                    "freenas",
                    schedule_option="no_schedule"
                ),
                "pushxtremio": SchedOpts(
                    "xtremio",
                    schedule_option="no_schedule"
                ),
                "pushgcedisks": SchedOpts(
                    "gcedisks",
                    schedule_option="no_schedule"
                ),
                "pushhds": SchedOpts(
                    "hds",
                    schedule_option="no_schedule"
                ),
                "pushnecism": SchedOpts(
                    "necism",
                    schedule_option="no_schedule"
                ),
                "pusheva": SchedOpts(
                    "eva",
                    schedule_option="no_schedule"
                ),
                "pushibmsvc": SchedOpts(
                    "ibmsvc",
                    schedule_option="no_schedule"
                ),
                "pushvioserver": SchedOpts(
                    "vioserver",
                    schedule_option="no_schedule"
                ),
                "pushsym": SchedOpts(
                    "sym",
                    schedule_option="no_schedule"
                ),
                "pushbrocade": SchedOpts(
                    "brocade", schedule_option="no_schedule"
                ),
                "pushdisks": SchedOpts(
                    "disks"
                ),
                "sysreport": SchedOpts(
                    "sysreport"
                ),
                "compliance_auto": SchedOpts(
                    "compliance",
                    fname="last_comp_check",
                    schedule_option="comp_schedule"
                ),
                "rotate_root_pw": SchedOpts(
                    "rotate_root_pw",
                    fname="last_rotate_root_pw",
                    schedule_option="no_schedule"
                ),
                "auto_reboot": SchedOpts(
                    "reboot",
                    fname="last_auto_reboot",
                    schedule_option="no_schedule"
                )
            },
        )

    @lazy
    def collector(self):
        """
        Lazy initialization of the node Collector object.
        """
        self.log.debug("initialize node::collector")
        return xmlrpcClient.Collector(node=self)

    @lazy
    def nodename(self):
        """
        Lazy initialization of the node name.
        """
        self.log.debug("initialize node::nodename")
        return socket.gethostname().lower()

    @lazy
    def system(self):
        """
        Lazy initialization of the operating system object, which implements
        specific methods like crash or fast-reboot.
        """
        try:
            rcos = __import__('rcOs'+rcEnv.sysname)
        except ImportError:
            rcos = __import__('rcOs')
        return rcos.Os()

    @lazy
    def compliance(self):
        from compliance import Compliance
        comp = Compliance(self)
        return comp

    @staticmethod
    def check_privs(action):
        """
        Raise if the action requires root privileges but the current
        running user is not root.
        """
        if action in UNPRIVILEGED_ACTIONS:
            return
        check_privs()

    @lazy
    def dns(self):
        try:
            return self.conf_get("cluster", "dns")
        except ex.OptNotFound as exc:
            return exc.default

    @lazy
    def env(self):
        try:
            return self.conf_get("node", "env")
        except ex.OptNotFound as exc:
            return exc.default

    def get_min_avail(self, keyword, metric, limit=100):
        try:
            val = self.conf_get("node", keyword)
        except ex.OptNotFound as exc:
            val = exc.default
        if str(val).endswith("%"):
            val = int(val.rstrip("%"))
        else:
            total = self.stats().get(metric) # mb
            val = val // 1024 // 1024 # b > mb
            if total is None:
                return
            val = int(val/total*100)
            if val > limit:
                # unreasonable
                val = limit
        return val

    @lazy
    def min_avail_mem(self):
        return self.get_min_avail("min_avail_mem", "mem_total", 50)

    @lazy
    def min_avail_swap(self):
        return self.get_min_avail("min_avail_swap", "swap_total", 100)

    @lazy
    def max_parallel(self):
        try:
            return self.conf_get("node", "max_parallel")
        except ex.OptNotFound as exc:
            return self.default_max_parallel()

    def default_max_parallel(self):
        nr = int(self.asset.get_cpu_threads()["value"])
        if nr == 0:
            nr = int(self.asset.get_cpu_cores()["value"])
        return max(2, nr//2)

    @lazy
    def dnsnodes(self):
        import socket
        nodes = []
        for ip in self.dns:
            try:
                node = socket.gethostbyaddr(ip)[0]
            except Exception as exc:
                node = None
            if node in self.cluster_nodes:
                nodes.append(node)
        return nodes

    @staticmethod
    def split_url(url, default_app=None):
        """
        Split a node.conf node.dbopensvc style url into a
        (protocol, host, port, app) tuple.
        """
        if url == 'None':
            return 'https', '127.0.0.1', '443', '/'

        # transport
        if url.startswith('https'):
            transport = 'https'
            url = url.replace('https://', '')
        elif url.startswith('http'):
            transport = 'http'
            url = url.replace('http://', '')
        else:
            transport = 'https'

        elements = url.split('/')
        if len(elements) < 1:
            raise ex.excError("url %s should have at least one slash")

        # app
        if len(elements) > 1:
            app = elements[1]
        else:
            app = default_app

        # host/port
        subelements = elements[0].split(':')
        if len(subelements) == 1:
            host = subelements[0]
            if transport == 'http':
                port = '80'
            else:
                port = '443'
        elif len(subelements) == 2:
            host = subelements[0]
            port = subelements[1]
        else:
            raise ex.excError("too many columns in %s" % ":".join(subelements))

        return transport, host, port, app

    def set_collector_env(self):
        """
        Store the collector connection elements parsed from the node.conf
        node.uuid, node.dbopensvc and node.dbcompliance as properties of the
        rcEnv class.
        """
        if self.config is None:
            self.load_config()
        if self.config is None:
            return
        if self.config.has_option('node', 'dbopensvc'):
            url = self.config.get('node', 'dbopensvc')
            try:
                (
                    rcEnv.dbopensvc_transport,
                    rcEnv.dbopensvc_host,
                    rcEnv.dbopensvc_port,
                    rcEnv.dbopensvc_app
                ) = self.split_url(url, default_app="feed")
                rcEnv.dbopensvc = "%s://%s:%s/%s/default/call/xmlrpc" % (
                    rcEnv.dbopensvc_transport,
                    rcEnv.dbopensvc_host,
                    rcEnv.dbopensvc_port,
                    rcEnv.dbopensvc_app
                )
            except ex.excError as exc:
                self.log.error("malformed dbopensvc url: %s (%s)",
                               rcEnv.dbopensvc, str(exc))
        else:
            rcEnv.dbopensvc_transport = None
            rcEnv.dbopensvc_host = None
            rcEnv.dbopensvc_port = None
            rcEnv.dbopensvc_app = None
            rcEnv.dbopensvc = None

        if self.config.has_option('node', 'dbcompliance'):
            url = self.config.get('node', 'dbcompliance')
            try:
                (
                    rcEnv.dbcompliance_transport,
                    rcEnv.dbcompliance_host,
                    rcEnv.dbcompliance_port,
                    rcEnv.dbcompliance_app
                ) = self.split_url(url, default_app="init")
                rcEnv.dbcompliance = "%s://%s:%s/%s/compliance/call/xmlrpc" % (
                    rcEnv.dbcompliance_transport,
                    rcEnv.dbcompliance_host,
                    rcEnv.dbcompliance_port,
                    rcEnv.dbcompliance_app
                )
            except ex.excError as exc:
                self.log.error("malformed dbcompliance url: %s (%s)",
                               rcEnv.dbcompliance, str(exc))
        else:
            rcEnv.dbcompliance_transport = rcEnv.dbopensvc_transport
            rcEnv.dbcompliance_host = rcEnv.dbopensvc_host
            rcEnv.dbcompliance_port = rcEnv.dbopensvc_port
            rcEnv.dbcompliance_app = "init"
            rcEnv.dbcompliance = "%s://%s:%s/%s/compliance/call/xmlrpc" % (
                rcEnv.dbcompliance_transport,
                rcEnv.dbcompliance_host,
                rcEnv.dbcompliance_port,
                rcEnv.dbcompliance_app
            )

        if self.config.has_option('node', 'uuid'):
            rcEnv.uuid = self.config.get('node', 'uuid')
        else:
            rcEnv.uuid = ""

    def call(self, *args, **kwargs):
        """
        Wrap rcUtilities call function, setting the node logger.
        """
        kwargs["log"] = self.log
        return call(*args, **kwargs)

    def vcall(self, *args, **kwargs):
        """
        Wrap rcUtilities vcall function, setting the node logger.
        """
        kwargs["log"] = self.log
        return vcall(*args, **kwargs)

    def svcs_selector(self, selector):
        """
        Given a selector string, return a list of service names.
        This exposed method only aggregates ORed elements.
        """
        if os.environ.get("OSVC_SERVICE_LINK"):
            return [os.environ.get("OSVC_SERVICE_LINK")]
        self.build_services(minimal=True)
        try:
            return self._svcs_selector(selector)
        finally:
            del self.services
            self.services = None

    def _svcs_selector(self, selector):
        if selector is None:
            return [svc.svcname for svc in self.svcs]
        elif "," in selector:
            ored_selectors = selector.split(",")
        else:
            ored_selectors = [selector]
        if selector in (None, ""):
            result = [svc.svcname for svc in self.svcs]
        else:
            result = []
            for _selector in ored_selectors:
                for svcname in self.__svcs_selector(_selector):
                    if svcname not in result:
                        result.append(svcname)
        import re
        if len(result) == 0 and re.findall(r"[,\+\*=\^:~><]", selector) == []:
            raise ex.excError("service not found")
        return result

    def __svcs_selector(self, selector):
        """
        Given a selector string, return a list of service names.
        This method only intersect the ANDed elements.
        """
        if selector is None:
            return
        elif "+" in selector:
            anded_selectors = selector.split("+")
        else:
            anded_selectors = [selector]
        if selector in (None, ""):
            result = [svc.svcname for svc in self.svcs]
        else:
            result = None
            for _selector in anded_selectors:
                svcnames = self.___svcs_selector(_selector)
                if result is None:
                    result = svcnames
                else:
                    common = set(result) & set(svcnames)
                    result = [svcname for svcname in result if svcname in common]
        return result

    def ___svcs_selector(self, selector):
        """
        Given a basic selector string (no AND nor OR), return a list of service
        names.
        """
        import fnmatch
        import re

        ops = r"(<=|>=|<|>|=|~|:)"
        negate = selector[0] == "!"
        selector = selector.lstrip("!")
        elts = re.split(ops, selector)

        def matching(current, op, value):
            if op in ("<", ">", ">=", "<="):
                try:
                    current = float(current)
                except ValueError:
                    return False
            if op == "=":
                match = current == value
            elif op == "~":
                match = re.search(value, current)
            elif op == ">":
                match = current > value
            elif op == ">=":
                match = current >= value
            elif op == "<":
                match = current < value
            elif op == "<=":
                match = current <= value
            elif op == ":":
                match = True
            return match

        def svc_matching(svc, param, op, value):
            try:
                current = svc._get(param, evaluate=True)
            except (ex.excError, ex.OptNotFound, ex.RequiredOptNotFound):
                current = None
            if current is None:
                if "." in param:
                    group, _param = param.split(".", 1)
                else:
                    group = param
                    _param = None
                rids = [section for section in svc.config.sections() if section.split('#')[0] == group]
                if op == ":" and len(rids) > 0 and _param is None:
                    return True
                elif _param:
                    for rid in rids:
                        try:
                            _current = svc._get(rid+"."+_param, evaluate=True)
                        except (ex.excError, ex.OptNotFound, ex.RequiredOptNotFound):
                            continue
                        if matching(_current, op, value):
                            return True
                return False
            if current is None:
                if op == ":":
                    return True
            elif matching(current, op, value):
                return True
            return False

        if len(elts) == 1:
            return [svc.svcname for svc in self.svcs if negate ^ fnmatch.fnmatch(svc.svcname, selector)]
        elif len(elts) != 3:
            return []
        param, op, value = elts
        if op in ("<", ">", ">=", "<="):
            try:
                value = float(value)
            except ValueError:
                return []
        result = []
        for svc in self.svcs:
            ret = svc_matching(svc, param, op, value)
            if ret ^ negate:
                result.append(svc.svcname)

        return result


    def build_services(self, *args, **kwargs):
        """
        Instanciate a Svc objects for each requested services and add it to
        the node.
        """
        if self.svcs is not None and \
           ('svcnames' not in kwargs or \
           (isinstance(kwargs['svcnames'], list) and len(kwargs['svcnames']) == 0)):
            return

        if 'svcnames' in kwargs and \
           isinstance(kwargs['svcnames'], list) and \
           len(kwargs['svcnames']) > 0 and \
           self.svcs is not None:
            svcnames_request = set(kwargs['svcnames'])
            svcnames_actual = set([s.svcname for s in self.svcs])
            if len(svcnames_request-svcnames_actual) == 0:
                return

        self.services = {}

        kwargs["node"] = self
        svcs, errors = svcBuilder.build_services(*args, **kwargs)
        if 'svcnames' in kwargs:
            self.check_build_errors(kwargs['svcnames'], svcs, errors)

        opt_status = kwargs.get("status")
        for svc in svcs:
            if opt_status is not None and not svc.status() in opt_status:
                continue
            self += svc

        rcLogger.set_namelen(self.svcs)

    @staticmethod
    def check_build_errors(svcnames, svcs, errors):
        """
        Raise error if the service builder did not return a Svc object for
        each service we requested.
        """
        if isinstance(svcnames, list):
            n_args = len(svcnames)
        else:
            n_args = 1
        n_svcs = len(svcs)
        if n_svcs == n_args:
            return 0
        msg = ""
        if n_args > 1:
            msg += "%d services validated out of %d\n" % (n_svcs, n_args)
        if len(errors) == 1:
            msg += errors[0]
        else:
            msg += "\n".join(["- "+err for err in errors])
        raise ex.excError(msg)

    def rebuild_services(self, svcnames, minimal):
        """
        Delete the list of Svc objects in the Node object and create a new one.

        Args:
          svcnames: add only Svc objects for services specified
          minimal: include a minimal set of properties in the new Svc objects
        """
        del self.services
        self.services = None
        self.build_services(svcnames=svcnames, minimal=minimal,
                            node=self)

    def close(self):
        """
        Stop the node class workers
        """
        if lazy_initialized(self, "devnull"):
            os.close(self.devnull)

        import gc
        import threading
        gc.collect()
        for thr in threading.enumerate():
            if thr.name == 'QueueFeederThread' and thr.ident is not None:
                thr.join(1)


    def make_temp_config(self):
        """
        Copy the current service configuration file to a temporary
        location for edition.
        If the temp file already exists, propose the --discard
        or --recover options.
        """
        import shutil
        if os.path.exists(self.paths.tmp_cf):
            if self.options.recover:
                pass
            elif self.options.discard:
                shutil.copy(rcEnv.paths.nodeconf, self.paths.tmp_cf)
            else:
                self.edit_config_diff()
                print("%s exists: node conf is already being edited. Set "
                      "--discard to edit from the current configuration, "
                      "or --recover to open the unapplied config" % \
                      self.paths.tmp_cf, file=sys.stderr)
                raise ex.excError
        else:
            shutil.copy(rcEnv.paths.nodeconf, self.paths.tmp_cf)
        return self.paths.tmp_cf

    def edit_config_diff(self):
        """
        Display the diff between the current config and the pending
        unvalidated config.
        """
        from subprocess import call

        def diff_capable(opts):
            cmd = ["diff"] + opts + [rcEnv.paths.nodeconf, self.paths.cf]
            cmd_results = justcall(cmd)
            if cmd_results[2] == 0:
                return True
            return False

        if not os.path.exists(self.paths.tmp_cf):
            return
        if diff_capable(["-u", "--color"]):
            cmd = ["diff", "-u", "--color", rcEnv.paths.nodeconf, self.paths.tmp_cf]
        elif diff_capable(["-u"]):
            cmd = ["diff", "-u", rcEnv.paths.nodeconf, self.paths.tmp_cf]
        else:
            cmd = ["diff", rcEnv.paths.nodeconf, self.paths.tmp_cf]
        call(cmd)

    def edit_config(self):
        """
        Execute an editor on the node configuration file.
        When the editor exits, validate the new configuration file.
        If validation pass, install the new configuration,
        else keep the previous configuration in place and offer the
        user the --recover or --discard choices for its next edit
        config action.
        """
        if "EDITOR" in os.environ:
            editor = os.environ["EDITOR"]
        elif os.name == "nt":
            editor = "notepad"
        else:
            editor = "vi"
        from rcUtilities import which, fsum
        if not which(editor):
            print("%s not found" % editor, file=sys.stderr)
            return 1
        path = self.make_temp_config()
        os.environ["LANG"] = "en_US.UTF-8"
        os.system(' '.join((editor, path)))
        if fsum(path) == fsum(rcEnv.paths.nodeconf):
            os.unlink(path)
            return 0
        results = self._validate_config(path=path)
        if results["errors"] == 0:
            import shutil
            shutil.copy(path, rcEnv.paths.nodeconf)
            os.unlink(path)
        else:
            print("your changes were not applied because of the errors "
                  "reported above. you can use the edit config command "
                  "with --recover to try to fix your changes or with "
                  "--discard to restart from the live config")
        return results["errors"] + results["warnings"]

    def edit_authconfig(self):
        """
        edit_authconfig node action entrypoint
        """
        fpath = os.path.join(rcEnv.paths.pathetc, "auth.conf")
        return self.edit_cf(fpath)

    @staticmethod
    def edit_cf(fpath):
        """
        Choose an editor, setup the LANG, and exec the editor on the
        file passed as argument.
        """
        if "EDITOR" in os.environ:
            editor = os.environ["EDITOR"]
        elif os.name == "nt":
            editor = "notepad"
        else:
            editor = "vi"
        if not which(editor):
            print("%s not found" % editor, file=sys.stderr)
            return 1
        os.environ["LANG"] = "en_US.UTF-8"
        return os.system(' '.join((editor, fpath)))

    def write_config(self):
        """
        Rewrite node.conf using the in-memory ConfigParser object as reference.
        """
        for option in CONFIG_DEFAULTS:
            if self.config.has_option('DEFAULT', option):
                self.config.remove_option('DEFAULT', option)
        for section in self.config.sections():
            if '#sync#' in section:
                self.config.remove_section(section)
        import tempfile
        import shutil
        import codecs
        try:
            tmpf = tempfile.NamedTemporaryFile()
            fpath = tmpf.name
            tmpf.close()
            if sys.version_info[0] >= 3:
                with codecs.open(fpath, "w", "utf-8") as tmpf:
                    self.config.write(tmpf)
            else:
                with open(fpath, "w") as tmpf:
                    self.config.write(tmpf)
            shutil.move(fpath, rcEnv.paths.nodeconf)
        except (OSError, IOError) as exc:
            print("failed to write new %s (%s)" % (rcEnv.paths.nodeconf, str(exc)),
                  file=sys.stderr)
            raise ex.excError
        try:
            os.chmod(rcEnv.paths.nodeconf, 0o0600)
        except OSError:
            pass
        self.load_config()

    def purge_status_last(self):
        """
        Purge the cached status of each and every services and resources.
        """
        for svc in self.svcs:
            svc.purge_status_last()

    def load_config(self):
        """
        Parse the node.conf configuration file and store the RawConfigParser
        object as self.config.
        IOError is catched and self.config is left to its current value, which
        initially is None. So users of self.config should check for this None
        value before use.
        """
        try:
            self.config = read_cf(rcEnv.paths.nodeconf, CONFIG_DEFAULTS)
        except rcConfigParser.ParsingError as exc:
            print(str(exc), file=sys.stderr)
        except IOError:
            # some action don't need self.config
            pass

    def load_auth_config(self):
        """
        Parse the auth.conf configuration file and store the RawConfigParser
        object as self.auth_config.
        The actual parsing is done only on first call. This method is a noop
        on subsequent calls.
        """
        if self.auth_config is not None:
            return
        try:
            self.auth_config = read_cf(rcEnv.paths.authconf)
        except rcConfigParser.ParsingError as exc:
            print(str(exc), file=sys.stderr)
        except IOError:
            # some action don't need self.auth_config
            pass

    def setup_sync_outdated(self):
        """
        Return True if any configuration file has changed in the last 10'
        else return False
        """
        import glob
        setup_sync_flag = os.path.join(self.var_d, 'last_setup_sync')
        fpaths = glob.glob(os.path.join(rcEnv.paths.pathetc, '*.conf'))
        if not os.path.exists(setup_sync_flag):
            return True
        for fpath in fpaths:
            try:
                mtime = os.stat(fpath).st_mtime
                with open(setup_sync_flag) as ofile:
                    last = float(ofile.read())
            except (OSError, IOError):
                return True
            if mtime > last:
                return True
        return False

    def __iadd__(self, svc):
        """
        Implement the Node() += Svc() operation, setting the node backpointer
        in the added service, storing the service in a list
        """
        if not hasattr(svc, "svcname"):
            return self
        if self.services is None:
            self.services = {}
        self.services[svc.svcname] = svc
        return self

    def action(self, action, options=None):
        """
        The node action wrapper.
        Looks up which method to handle the action (some are not implemented
        in the Node class), and call the handling method.
        """
        try:
            self.async_action(action)
        except ex.excAbortAction:
            return 0
        return self._action(action, options)

    @sched_action
    def _action(self, action, options=None):
        if "_json_" in action:
            self.options.format = "json"
            action = action.replace("_json_", "_")
        if action.startswith("json_"):
            self.options.format = "json"
            action = "print" + action[4:]

        if action.startswith("compliance_"):
            if self.options.cron and action == "compliance_auto" and \
               self.config.has_option('compliance', 'auto_update') and \
               self.config.getboolean('compliance', 'auto_update'):
                self.compliance.updatecomp = True
                self.compliance.node = self
            ret = getattr(self.compliance, action)()
        elif action.startswith("collector_") and action != "collector_cli":
            from collector import Collector
            coll = Collector(self.options, self)
            data = getattr(coll, action)()
            self.print_data(data)
            ret = 0
        elif action.startswith("print"):
            getattr(self, action)()
            ret = 0
        else:
            ret = getattr(self, action)()

        if action in REMOTE_ACTIONS and os.environ.get("OSVC_ACTION_ORIGIN") != "daemon":
            self.wake_monitor()

        if ret is None:
            ret = 0
        elif isinstance(ret, bool):
            if ret:
                return 1
            else:
                return 0
        return ret

    @formatter
    def print_data(self, data):
        """
        A dummy method decorated by the formatter function.
        The formatter needs self to access the formatting options, so this
        can't be a staticmethod.
        """
        fmt = self.options.format if self.options.format else "default"
        self.log.debug("format data using the %s formatter", fmt)
        return data

    def print_schedule(self):
        """
        The 'print schedule' node and service action entrypoint.
        """
        return self.sched.print_schedule()

    def get_push_objects(self, section):
        """
        Returns the object names to do inventory on.
        Object names passed as nodemgr argument take precedence.
        If not specified by argument, objet names found in the
        configuration file section are returned.
        """
        if len(self.options.objects) > 0:
            return self.options.objects
        if self.config and self.config.has_option(section, "objects"):
            return self.config.get(section, "objects").split(",")
        return []

    def collect_stats(self):
        """
        Choose the os specific stats collection module and call its collect
        method.
        """
        try:
            mod = __import__("rcStatsCollect"+rcEnv.sysname)
        except ImportError:
            return
        mod.collect(self)

    def pushstats(self):
        """
        Set stats range to push to "last successful pushstat => now"

        Enforce a minimum interval of 21m, and a maximum of 1450m.

        The scheduled task that collects system statistics from system tools
        like sar, and sends the data to the collector.
        A list of metrics can be disabled from the task configuration section,
        using the 'disable' option.
        """
        fpath = self.sched.get_timestamp_f(self.sched.scheduler_actions["pushstats"].fname,
                                           success=True)
        try:
            with open(fpath, "r") as ofile:
                buff = ofile.read()
            start = datetime.datetime.strptime(buff, "%Y-%m-%d %H:%M:%S.%f\n")
            now = datetime.datetime.now()
            delta = now - start
            interval = delta.days * 1440 + delta.seconds // 60 + 10
        except:
            interval = 1450
        if interval < 21:
            interval = 21

        def get_disable_stats():
            """
            Returns the list of stats metrics collection disabled through the
            configuration file stats.disable option.
            """
            if not self.config.has_option("stats", "disable"):
                return []
            disable = self.config.get("stats", "disable")
            try:
                return json.loads(disable)
            except ValueError:
                pass
            if ',' in disable:
                return disable.replace(' ', '').split(',')
            return disable.split()

        disable = get_disable_stats()
        return self.collector.call('push_stats',
                                   stats_dir=self.options.stats_dir,
                                   stats_start=self.options.begin,
                                   stats_end=self.options.end,
                                   interval=interval,
                                   disable=disable)

    @lazy
    def asset(self):
        try:
            m = __import__('rcAsset'+rcEnv.sysname)
        except ImportError:
            raise ex.excError("pushasset methods not implemented on %s"
                              "" % rcEnv.sysname)
        return m.Asset(self)

    def pushpkg(self):
        """
        The pushpkg action entrypoint.
        Inventories the installed packages.
        """
        self.collector.call('push_pkg')

    def pushpatch(self):
        """
        The pushpatch action entrypoint.
        Inventories the installed patches.
        """
        self.collector.call('push_patch')

    def pushasset(self):
        """
        The pushasset action entrypoint.
        Inventories the server properties.
        """
        data = self.asset.get_asset_dict()
        try:
            if self.options.format is None:
                self.print_asset(data)
                return
            self.print_data(data)
        finally:
            self.collector.call('push_asset', self, data)

    def print_asset(self, data):
        from forest import Forest
        from rcColor import color
        tree = Forest()
        head_node = tree.add_node()
        head_node.add_column(rcEnv.nodename, color.BOLD)
        head_node.add_column("Value", color.BOLD)
        head_node.add_column("Source", color.BOLD)
        for key, _data in data.items():
            node = head_node.add_node()
            if key not in ("targets", "lan", "uids", "gids", "hba", "hardware"):
                if _data["value"] is None:
                    _data["value"] = ""
                node.add_column(_data["title"], color.LIGHTBLUE)
                if "formatted_value" in _data:
                    node.add_column(_data["formatted_value"])
                else:
                    node.add_column(_data["value"])
                node.add_column(_data["source"])

        if 'uids' in data:
            node = head_node.add_node()
            node.add_column("uids", color.LIGHTBLUE)
            node.add_column(str(len(data['uids'])))
            node.add_column(self.asset.s_probe)

        if 'gids' in data:
            node = head_node.add_node()
            node.add_column("gids", color.LIGHTBLUE)
            node.add_column(str(len(data['gids'])))
            node.add_column(self.asset.s_probe)

        if 'hardware' in data:
            node = head_node.add_node()
            node.add_column("hardware", color.LIGHTBLUE)
            node.add_column(str(len(data['hardware'])))
            node.add_column(self.asset.s_probe)
            for _data in data['hardware']:
                _node = node.add_node()
                _node.add_column("%s %s" % (_data["type"], _data["path"]))
                _node.add_column("%s: %s [%s]" % (_data["class"], _data["description"], _data["driver"]))

        if 'hba' in data:
            node = head_node.add_node()
            node.add_column("host bus adapters", color.LIGHTBLUE)
            node.add_column(str(len(data['hba'])))
            node.add_column(self.asset.s_probe)
            for _data in data['hba']:
                _node = node.add_node()
                _node.add_column(_data["hba_id"])
                _node.add_column(_data["hba_type"])

                if 'targets' in data:
                    hba_targets = [_d for _d in data['targets'] if _d["hba_id"] == _data["hba_id"]]
                    if len(hba_targets) == 0:
                        continue
                    __node = _node.add_node()
                    __node.add_column("targets")
                    for _d in hba_targets:
                        ___node = __node.add_node()
                        ___node.add_column(_d["tgt_id"])

        if 'lan' in data:
            node = head_node.add_node()
            node.add_column("ip addresses", color.LIGHTBLUE)
            n = 0
            for mac, _data in data['lan'].items():
                for __data in _data:
                    _node = node.add_node()
                    addr = __data["addr"]
                    if __data["mask"]:
                        addr += "/" + __data["mask"]
                    _node.add_column(addr)
                    _node.add_column(__data["intf"])
                    n += 1
            node.add_column(str(n))
            node.add_column(self.asset.s_probe)

        tree.print()


    def pushnsr(self):
        """
        The pushnsr action entrypoint.
        Inventories Networker Backup Server index databases.
        """
        self.collector.call('push_nsr')

    def pushhp3par(self):
        """
        The push3par action entrypoint.
        Inventories HP 3par storage arrays.
        """
        self.collector.call('push_hp3par', self.options.objects)

    def pushnetapp(self):
        """
        The pushnetapp action entrypoint.
        Inventories NetApp storage arrays.
        """
        self.collector.call('push_netapp', self.options.objects)

    def pushcentera(self):
        """
        The pushcentera action entrypoint.
        Inventories Centera storage arrays.
        """
        self.collector.call('push_centera', self.options.objects)

    def pushemcvnx(self):
        """
        The pushemcvnx action entrypoint.
        Inventories EMC VNX storage arrays.
        """
        self.collector.call('push_emcvnx', self.options.objects)

    def pushibmds(self):
        """
        The pushibmds action entrypoint.
        Inventories IBM DS storage arrays.
        """
        self.collector.call('push_ibmds', self.options.objects)

    def pushgcedisks(self):
        """
        The pushgcedisks action entrypoint.
        Inventories Google Compute Engine disks.
        """
        self.collector.call('push_gcedisks', self.options.objects)

    def pushfreenas(self):
        """
        The pushfreenas action entrypoint.
        Inventories FreeNas storage arrays.
        """
        self.collector.call('push_freenas', self.options.objects)

    def pushxtremio(self):
        """
        The pushxtremio action entrypoint.
        Inventories XtremIO storage arrays.
        """
        self.collector.call('push_xtremio', self.options.objects)

    def pushdcs(self):
        """
        The pushdcs action entrypoint.
        Inventories DataCore SAN Symphony storage arrays.
        """
        self.collector.call('push_dcs', self.options.objects)

    def pushhds(self):
        """
        The pushhds action entrypoint.
        Inventories Hitachi storage arrays.
        """
        self.collector.call('push_hds', self.options.objects)

    def pushnecism(self):
        """
        The pushnecism action entrypoint.
        Inventories NEC iSM storage arrays.
        """
        self.collector.call('push_necism', self.options.objects)

    def pusheva(self):
        """
        The pusheva action entrypoint.
        Inventories HP EVA storage arrays.
        """
        self.collector.call('push_eva', self.options.objects)

    def pushibmsvc(self):
        """
        The pushibmsvc action entrypoint.
        Inventories IBM SVC storage arrays.
        """
        self.collector.call('push_ibmsvc', self.options.objects)

    def pushvioserver(self):
        """
        The pushvioserver action entrypoint.
        Inventories IBM vio server storage arrays.
        """
        self.collector.call('push_vioserver', self.options.objects)

    def pushsym(self):
        """
        The pushsym action entrypoint.
        Inventories EMC Symmetrix server storage arrays.
        """
        objects = self.get_push_objects("sym")
        self.collector.call('push_sym', objects)

    def pushbrocade(self):
        """
        The pushsym action entrypoint.
        Inventories Brocade SAN switches.
        """
        self.collector.call('push_brocade', self.options.objects)

    def auto_rotate_root_pw(self):
        """
        The rotate_root_pw node action entrypoint.
        """
        self.rotate_root_pw()

    def unschedule_reboot(self):
        """
        Unflag the node for reboot during the next allowed period.
        """
        if not os.path.exists(self.paths.reboot_flag):
            print("reboot already not scheduled")
            return
        os.unlink(self.paths.reboot_flag)
        print("reboot unscheduled")

    def schedule_reboot(self):
        """
        Flag the node for reboot during the next allowed period.
        """
        if not os.path.exists(self.paths.reboot_flag):
            with open(self.paths.reboot_flag, "w") as ofile:
                ofile.write("")
        import stat
        statinfo = os.stat(self.paths.reboot_flag)
        if statinfo.st_uid != 0:
            os.chown(self.paths.reboot_flag, 0, -1)
            print("set %s root ownership"%self.paths.reboot_flag)
        if statinfo.st_mode & stat.S_IWOTH:
            mode = statinfo.st_mode ^ stat.S_IWOTH
            os.chmod(self.paths.reboot_flag, mode)
            print("set %s not world-writable"%self.paths.reboot_flag)
        print("reboot scheduled")

    def schedule_reboot_status(self):
        """
        Display information about the next scheduled reboot
        """
        import stat
        if os.path.exists(self.paths.reboot_flag):
            statinfo = os.stat(self.paths.reboot_flag)
        else:
            statinfo = None

        if statinfo is None or \
           statinfo.st_uid != 0 or statinfo.st_mode & stat.S_IWOTH:
            print("reboot is not scheduled")
            return

        sch = self.sched.scheduler_actions["auto_reboot"]
        schedule = self.sched.sched_get_schedule_raw(sch.section, sch.schedule_option)
        print("reboot is scheduled")
        print("reboot schedule: %s" % schedule)

        result = self.sched.get_next_schedule("auto_reboot")
        if result["next_sched"]:
            print("next reboot slot:",
                  result["next_sched"].strftime("%a %Y-%m-%d %H:%M"))
        elif result["minutes"] is None:
            print("next reboot slot: none")
        else:
            print("next reboot slot: none in the next %d days" % (result["minutes"]/144))

    def auto_reboot(self):
        """
        The scheduler task executing the node reboot if the scheduler
        constraints are satisfied and the reboot flag is set.
        """
        if not os.path.exists(self.paths.reboot_flag):
            print("%s is not present. no reboot scheduled" % self.paths.reboot_flag)
            return
        import stat
        statinfo = os.stat(self.paths.reboot_flag)
        if statinfo.st_uid != 0:
            print("%s does not belong to root. abort scheduled reboot" % self.paths.reboot_flag)
            return
        if statinfo.st_mode & stat.S_IWOTH:
            print("%s is world writable. abort scheduled reboot" % self.paths.reboot_flag)
            return
        try:
            once = self.config.get("reboot", "once")
            once = convert_boolean(once)
        except:
            once = True
        if once:
            print("remove %s and reboot" % self.paths.reboot_flag)
            os.unlink(self.paths.reboot_flag)
        self.reboot()

    def pushdisks(self):
        """
        The pushdisks node action entrypoint.

        Send to the collector the list of disks visible on this node, and
        their use by service.
        """
        data = self.push_disks_data()
        if self.options.format is None:
            self.print_push_disks(data)
        else:
            self.print_data(data)
        self.collector.call('push_disks', data)

    def print_push_disks(self, data):
        from forest import Forest
        from rcColor import color
        from converters import print_size

        tree = Forest()
        head_node = tree.add_node()
        head_node.add_column(rcEnv.nodename, color.BOLD)
        head_node.add_column("Size.Used", color.BOLD)
        head_node.add_column("Vendor", color.BOLD)
        head_node.add_column("Model", color.BOLD)

        if len(data["disks"]) > 0:
            disks_node = head_node.add_node()
            disks_node.add_column("disks", color.BROWN)

        if len(data["served_disks"]) > 0:
            sdisks_node = head_node.add_node()
            sdisks_node.add_column("served disks", color.BROWN)

        for disk_id, disk in data["disks"].items():
            disk_node = disks_node.add_node()
            disk_node.add_column(disk_id, color.LIGHTBLUE)
            disk_node.add_column(print_size(disk["size"]))
            disk_node.add_column(disk["vendor"])
            disk_node.add_column(disk["model"])

            for svcname, service in disk["services"].items():
                svc_node = disk_node.add_node()
                svc_node.add_column(svcname, color.LIGHTBLUE)
                svc_node.add_column(print_size(service["used"]))

            if disk["used"] < disk["size"]:
                svc_node = disk_node.add_node()
                svc_node.add_column(rcEnv.nodename, color.LIGHTBLUE)
                svc_node.add_column(print_size(disk["size"] - disk["used"]))

        for disk_id, disk in data["served_disks"].items():
            disk_node = disks_node.add_node()
            disk_node.add_column(disk_id, color.LIGHTBLUE)
            disk_node.add_column(print_size(disk["size"]))
            disk_node.add_column(disk["vdisk_id"])

        tree.print()

    def push_stats_fs_u(self, l, sync=True):
        args = [l[0], l[1]]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        self.proxy.insert_stats_fs_u(*args)

    def push_disks_data(self):
        if self.svcs is None:
            self.build_services()

        import re
        di = __import__('rcDiskInfo'+rcEnv.sysname)
        data = {
            "disks": {},
            "served_disks": {},
        }

        for svc in self.svcs:
            # hash to add up disk usage inside a service
            for r in svc.get_resources():
                if hasattr(r, "name"):
                    disk_dg = r.name
                elif hasattr(r, "dev"):
                    disk_dg = r.dev
                else:
                    disk_dg = r.rid

                if hasattr(r, 'devmap') and hasattr(r, 'vm_hostname'):
                    for dev_id, vdev_id in r.devmap():
                        try:
                            disk_id = self.diskinfo.disk_id(dev_id)
                        except:
                            continue
                        try:
                            disk_size = self.diskinfo.disk_size(dev_id)
                        except:
                            continue
                        data["served_disks"][disk_id] = {
                            "dev_id": dev_id,
                            "vdev_id": vdev_id,
                            "vdisk_id": r.vm_hostname+'.'+vdev_id,
                            "size": disk_size,
                            "cluster": self.cluster_name,
                        }

                try:
                    devpaths = r.sub_devs()
                except Exception as e:
                    print(e)
                    devpaths = []

                for devpath in devpaths:
                    for d, used, region in self.devtree.get_top_devs_usage_for_devpath(devpath):
                        disk_id = self.diskinfo.disk_id(d)
                        if disk_id is None or disk_id == "":
                            continue
                        if disk_id.startswith(rcEnv.nodename+".loop"):
                            continue
                        disk_size = self.devtree.get_dev_by_devpath(d).size

                        if disk_id not in data["disks"]:
                            data["disks"][disk_id] = {
                                "size": disk_size,
                                "vendor": self.diskinfo.disk_vendor(d),
                                "model": self.diskinfo.disk_model(d),
                                "used": 0,
                                "services": {},
                            }

                        if svc.svcname not in data["disks"][disk_id]["services"]:
                            data["disks"][disk_id]["services"][svc.svcname] = {
                                "svcname": svc.svcname,
                                "used": used,
                                "dg": disk_dg,
                                "region": region
                            }
                        else:
                            # consume space at service level
                            data["disks"][disk_id]["services"][svc.svcname]["dg"] = "multi"
                            data["disks"][disk_id]["services"][svc.svcname]["used"] += used
                            if data["disks"][disk_id]["services"][svc.svcname]["used"] > disk_size:
                                data["disks"][disk_id]["services"][svc.svcname]["used"] = disk_size

                        # consume space at disk level
                        data["disks"][disk_id]["used"] += used
                        if data["disks"][disk_id]["used"] > disk_size:
                            data["disks"][disk_id]["used"] = disk_size

        done = []

        try:
            devpaths = self.devlist()
        except Exception as e:
            print(e)
            devpaths = []

        for devpath in devpaths:
            disk_id = self.diskinfo.disk_id(devpath)
            if disk_id is None or disk_id == "":
                continue
            if disk_id.startswith(rcEnv.nodename+".loop"):
                continue
            if re.match(r"/dev/rdsk/.*s[01345678]", devpath):
                # don't report partitions
                continue

            # Linux Node:devlist() reports paths, so we can have duplicate
            # disks here.
            if disk_id in done:
                continue
            done.append(disk_id)
            if disk_id in data["disks"]:
                continue

            disk_size = self.devtree.get_dev_by_devpath(devpath).size

            data["disks"][disk_id] = {
                "size": disk_size,
                "vendor": self.diskinfo.disk_vendor(devpath),
                "model": self.diskinfo.disk_model(devpath),
                "used": 0,
                "services": {},
            }

        return data

    def shutdown(self):
        """
        The shutdown node action entrypoint.
        To be overloaded by child classes.
        """
        self.log.warning("to be implemented")

    def reboot(self):
        """
        The reboot node action entrypoint.
        """
        self.do_triggers("reboot", "pre")
        self.log.info("reboot")
        self._reboot()

    def do_triggers(self, action, when):
        """
        Determine which triggers need to be executed for the action and
        executes them when appropriate.
        """
        trigger = None
        blocking_trigger = None
        try:
            trigger = self.config.get(action, when)
        except:
            pass
        try:
            blocking_trigger = self.config.get(action, "blocking_"+when)
        except:
            pass
        if trigger:
            self.log.info("execute trigger %s", trigger)
            try:
                self.do_trigger(trigger)
            except ex.excError:
                pass
        if blocking_trigger:
            self.log.info("execute blocking trigger %s", blocking_trigger)
            try:
                self.do_trigger(blocking_trigger)
            except ex.excError:
                if when == "pre":
                    self.log.error("blocking pre trigger error: abort %s", action)
                raise

    def do_trigger(self, cmd, err_to_warn=False):
        """
        The trigger execution wrapper.
        """
        import shlex
        _cmd = shlex.split(cmd)
        ret, out, err = self.vcall(_cmd, err_to_warn=err_to_warn)
        if ret != 0:
            raise ex.excError((ret, out, err))

    def _reboot(self):
        """
        A system reboot method to be implemented by child classes.
        """
        self.log.warning("to be implemented")

    def sysreport(self):
        """
        The sysreport node action entrypoint.

        Send to the collector a tarball of the files the user wants to track
        that changed since the last call.
        If the force option is set, send all files the user wants to track.
        """
        try:
            mod = __import__('rcSysReport'+rcEnv.sysname)
        except ImportError:
            print("sysreport is not supported on this os")
            return
        mod.SysReport(node=self).sysreport(force=self.options.force)

    def get_prkey(self):
        """
        Returns the persistent reservation key.
        Once generated from the algorithm, the prkey is written to the config
        file to ensure its stability.
        """
        if self.config.has_option("node", "prkey"):
            hostid = self.config.get("node", "prkey")
            if len(hostid) > 18 or not hostid.startswith("0x") or \
               len(set(hostid[2:]) - set("0123456789abcdefABCDEF")) > 0:
                raise ex.excError("prkey in node.conf must have 16 significant"
                                  " hex digits max (ex: 0x90520a45138e85)")
            return hostid
        self.log.info("can't find a prkey forced in node.conf. generate one.")
        hostid = "0x"+self.hostid()
        if not self.config.has_section("node"):
            self.config.add_section("node")
        self.config.set('node', 'prkey', hostid)
        self.write_config()
        return hostid

    def prkey(self):
        """
        Print the persistent reservation key.
        """
        print(self.get_prkey())

    @staticmethod
    def hostid():
        """
        Return a stable host unique id
        """
        mod = __import__('hostid'+rcEnv.sysname)
        return mod.hostid()

    def checks(self):
        """
        The checks node action entrypoint.
        Runs health checks.
        """
        import checks
        if self.svcs is None:
            self.build_services()
        checkers = checks.checks(self.svcs)
        checkers.node = self
        data = checkers.do_checks()
        self.print_data(data)

    def wol(self):
        """
        Send a Wake-On-LAN packet to a mac address on the broadcast address.
        """
        import rcWakeOnLan
        if self.options.mac is None:
            print("missing parameter. set --mac argument. multiple mac "
                  "addresses must be separated by comma", file=sys.stderr)
            print("example 1 : --mac 00:11:22:33:44:55", file=sys.stderr)
            print("example 2 : --mac 00:11:22:33:44:55,66:77:88:99:AA:BB",
                  file=sys.stderr)
            return 1
        if self.options.broadcast is None:
            print("missing parameter. set --broadcast argument. needed to "
                  "identify accurate network to use", file=sys.stderr)
            print("example 1 : --broadcast 10.25.107.255", file=sys.stderr)
            print("example 2 : --broadcast 192.168.1.5,10.25.107.255",
                  file=sys.stderr)
            return 1
        macs = self.options.mac.split(',')
        broadcasts = self.options.broadcast.split(',')
        udpports = str(self.options.port).split(',')
        for brdcast in broadcasts:
            for mac in macs:
                for port in udpports:
                    req = rcWakeOnLan.wolrequest(macaddress=mac, broadcast=brdcast, udpport=port)
                    if not req.check_broadcast():
                        print("Error : skipping broadcast address <%s>, not in "
                              "the expected format 123.123.123.123" % req.broadcast,
                              file=sys.stderr)
                        break
                    if not req.check_mac():
                        print("Error : skipping mac address <%s>, not in the "
                              "expected format 00:11:22:33:44:55" % req.mac,
                              file=sys.stderr)
                        continue
                    if req.send():
                        print("Sent Wake On Lan packet to mac address <%s>"%req.mac)
                    else:
                        print("Error while trying to send Wake On Lan packet to "
                              "mac address <%s>" % req.mac, file=sys.stderr)

    def allocate_rid(self, group, sections):
        """
        Return an unused rid in <group>.
        """
        prefix = group + "#"
        rids = [section for section in sections if section.startswith(prefix)]
        idx = 1
        while True:
            rid = "#".join((group, str(idx)))
            if rid in rids:
                idx += 1
                continue
            return rid

    def register_as_user(self):
        """
        Returns a node registration unique id, authenticating to the
        collector as a user.
        """
        data = self.collector.call('register_node')
        if data is None:
            raise ex.excError("failed to obtain a registration number")
        elif isinstance(data, dict) and "ret" in data and data["ret"] != 0:
            msg = "failed to obtain a registration number"
            if "msg" in data and len(data["msg"]) > 0:
                msg += "\n" + data["msg"]
            raise ex.excError(msg)
        elif isinstance(data, list):
            raise ex.excError(data[0])
        return data

    def register_as_node(self):
        """
        Returns a node registration unique id, authenticating to the
        collector as a node.
        """
        try:
            data = self.collector_rest_post("/register", {
                "nodename": rcEnv.nodename,
                "app": self.options.app
            })
        except Exception as exc:
            raise ex.excError(str(exc))
        if "error" in data:
            raise ex.excError(data["error"])
        return data["data"]["uuid"]

    def register(self):
        """
        Do anonymous or indentified node register to obtain a node uuid
        that will be used as a password valid for the current hostname used
        as a username in the application code context.
        """
        if self.options.user is None:
            register_fn = "register_as_user"
        else:
            register_fn = "register_as_node"
        try:
            rcEnv.uuid = getattr(self, register_fn)()
        except ex.excError as exc:
            print(exc, file=sys.stderr)
            return 1

        if not self.config.has_section('node'):
            self.config.add_section('node')
        self.config.set('node', 'uuid', rcEnv.uuid)

        try:
            self.write_config()
        except ex.excError:
            print("failed to write registration number: %s" % rcEnv.uuid,
                  file=sys.stderr)
            return 1

        print("registered")
        self.pushasset()
        self.pushdisks()
        self.pushpkg()
        self.pushpatch()
        self.sysreport()
        self.checks()
        return 0

    def service_action_worker(self, svc, action, options):
        """
        The method the per-service subprocesses execute
        """
        try:
            ret = svc.action(action, options)
        except Exception:
            self.close()
            sys.exit(1)
        finally:
            self.close()
        sys.exit(ret)

    @lazy
    def diskinfo(self):
        di = __import__('rcDiskInfo'+rcEnv.sysname)
        return di.diskInfo()

    @lazy
    def devtree(self):
        try:
            mod = __import__("rcDevTree"+rcEnv.sysname)
        except ImportError:
            return
        tree = mod.DevTree()
        tree.load()
        return tree

    def devlist(self):
        """
        Return the node's top-level device paths
        """
        devpaths = []
        for dev in self.devtree.get_top_devs():
            if len(dev.devpath) > 0:
                devpaths.append(dev.devpath[0])
        return devpaths

    def updatecomp(self):
        """
        Downloads and installs the compliance module archive from the url
        specified as node.repocomp or node.repo in node.conf.
        """
        if self.config.has_option('node', 'repocomp'):
            pkg_name = self.config.get('node', 'repocomp').strip('/') + "/current"
        elif self.config.has_option('node', 'repo'):
            pkg_name = self.config.get('node', 'repo').strip('/') + "/compliance/current"
        else:
            if self.options.cron:
                return 0
            print("node.repo or node.repocomp must be set in node.conf",
                  file=sys.stderr)
            return 1
        import tempfile
        tmpf = tempfile.NamedTemporaryFile()
        fpath = tmpf.name
        tmpf.close()
        try:
            ret = self._updatecomp(pkg_name, fpath)
        finally:
            if os.path.exists(fpath):
                os.unlink(fpath)
        return ret

    def _updatecomp(self, pkg_name, fpath):
        """
        Downloads and installs the compliance module archive from the url
        specified by the pkg_name argument. The download destination file
        is specified by fpath. The caller is responsible for its deletion.
        """
        print("get %s (%s)"%(pkg_name, fpath))
        try:
            self.urlretrieve(pkg_name, fpath)
        except IOError as exc:
            print("download failed", ":", exc, file=sys.stderr)
            if self.options.cron:
                return 0
            return 1
        tmpp = os.path.join(rcEnv.paths.pathtmp, 'compliance')
        backp = os.path.join(rcEnv.paths.pathtmp, 'compliance.bck')
        compp = os.path.join(rcEnv.paths.pathvar, 'compliance')
        if not os.path.exists(compp):
            os.makedirs(compp, 0o755)
        import shutil
        try:
            shutil.rmtree(backp)
        except (OSError, IOError):
            pass
        print("extract compliance in", rcEnv.paths.pathtmp)
        import tarfile
        tar = tarfile.open(fpath)
        os.chdir(rcEnv.paths.pathtmp)
        try:
            tar.extractall()
            tar.close()
        except (OSError, IOError):
            print("failed to unpack", file=sys.stderr)
            return 1
        print("install new compliance")
        for root, dirs, files in os.walk(tmpp):
            for fpath in dirs:
                os.chown(os.path.join(root, fpath), 0, 0)
                for fpath in files:
                    os.chown(os.path.join(root, fpath), 0, 0)
        shutil.move(compp, backp)
        shutil.move(tmpp, compp)
        return 0

    def updatepkg(self):
        """
        Downloads and upgrades the OpenSVC agent, using the system-specific
        packaging tools.
        """
        try:
            branch = self.config.get("node", "branch")
            if branch != "":
                branch = "/" + branch
        except Exception:
            branch = ""

        modname = 'rcUpdatePkg'+rcEnv.sysname
        if not os.path.exists(os.path.join(rcEnv.paths.pathlib, modname+'.py')):
            print("updatepkg not implemented on", rcEnv.sysname, file=sys.stderr)
            return 1
        mod = __import__(modname)
        if self.config.has_option('node', 'repopkg'):
            pkg_name = self.config.get('node', 'repopkg').strip('/') + \
                       "/" + mod.repo_subdir + branch + '/current'
        elif self.config.has_option('node', 'repo'):
            pkg_name = self.config.get('node', 'repo').strip('/') + \
                       "/packages/" + mod.repo_subdir + branch + '/current'
        else:
            print("node.repo or node.repopkg must be set in node.conf",
                  file=sys.stderr)
            return 1
        import tempfile
        tmpf = tempfile.NamedTemporaryFile()
        fpath = tmpf.name
        tmpf.close()
        print("get %s (%s)"%(pkg_name, fpath))
        try:
            self.urlretrieve(pkg_name, fpath)
        except IOError as exc:
            print("download failed", ":", exc, file=sys.stderr)
            try:
                os.unlink(fpath)
            except OSError:
                pass
            return 1
        print("updating opensvc")
        mod.update(fpath)
        print("clean up")
        try:
            os.unlink(fpath)
        except OSError:
            pass
        self.action("pushasset")
        self.build_services()
        return 0

    def array(self):
        """
        Execute a array command, passing extra_argv to the array driver.
        """
        array_name = None
        for idx, arg in enumerate(self.options.extra_argv):
            if arg.startswith("--array="):
                array_name = arg[arg.index("=")+1:]
                break
            if (arg == "-a" or arg == "--array") and idx+1 < len(self.options.extra_argv):
                array_name = self.options.extra_argv[idx+1]
                break

        if array_name is None:
            raise ex.excError("can not determine array driver (no --array)")

        self.load_auth_config()

        section = None

        for _section in self.auth_config.sections():
            if _section == array_name:
                section = _section
                break
            if self.auth_config.has_option(_section, "array") and \
               array_name in self.auth_config.get(_section, "array").split():
                section = _section
                break

        if section is None:
            raise ex.excError("array '%s' not found in %s" % (array_name, rcEnv.paths.authconf))

        if not self.auth_config.has_option(section, "type"):
            raise ex.excError("%s must have a '%s.type' option" % (rcEnv.paths.authconf, section))

        driver = self.auth_config.get(section, "type")
        rtype = driver[0].upper() + driver[1:].lower()
        modname = "rc" + rtype
        try:
            mod = __import__(modname)
        except ImportError as exc:
            raise ex.excError("driver %s load error: %s" % (modname, str(exc)))
        return mod.main(self.options.extra_argv, node=self)

    def get_ruser(self, node):
        """
        Returns the remote user to use for remote commands on the node
        specified as argument.
        If not specified as node.ruser in the node configuration file,
        the root user is returned.
        """
        default = "root"
        if not self.config.has_option('node', "ruser"):
            return default
        node_ruser = {}
        elements = self.config.get('node', 'ruser').split()
        for element in elements:
            subelements = element.split("@")
            if len(subelements) == 1:
                default = element
            elif len(subelements) == 2:
                _ruser, _node = subelements
                node_ruser[_node] = _ruser
            else:
                continue
        if node in node_ruser:
            return node_ruser[node]
        return default

    def dequeue_actions(self):
        """
        The dequeue_actions node action entrypoint.
        Poll the collector action queue until emptied.
        """
        actions = self.collector.call('collector_get_action_queue')
        if actions is None:
            return "unable to fetch actions scheduled by the collector"
        import re
        regex = re.compile(r"\x1b\[([0-9]{1,3}(;[0-9]{1,3})*)?[m|K|G]", re.UNICODE)
        data = []
        for action in actions:
            ret, out, err = self.dequeue_action(action)
            out = regex.sub('', out)
            err = regex.sub('', err)
            data.append((action.get('id'), ret, out, err))
        if len(actions) > 0:
            self.collector.call('collector_update_action_queue', data)

    @staticmethod
    def dequeue_action(action):
        """
        Execute the nodemgr or svcmgr action described in payload element
        received from the collector's action queue.
        """
        if action.get("svcname") is None or action.get("svcname") == "":
            cmd = [rcEnv.paths.nodemgr]
        else:
            cmd = [rcEnv.paths.svcmgr, "-s", action.get("svcname")]
        import shlex
        cmd += shlex.split(action.get("command", ""))
        print("dequeue action %s" % " ".join(cmd))
        out, err, ret = justcall(cmd)
        return ret, out, err

    def rotate_root_pw(self):
        """
        Generate a random password, send it to the collector and set it as
        the root user password.
        """
        passwd = self.genpw()

        from collector import Collector
        coll = Collector(self.options, self)
        try:
            getattr(coll, 'rotate_root_pw')(passwd)
        except Exception as exc:
            print("unexpected error sending the new password to the collector "
                  "(%s). Abording password change." % str(exc), file=sys.stderr)
            return 1

        try:
            mod = __import__('rcPasswd'+rcEnv.sysname)
        except ImportError:
            print("not implemented")
            return 1
        ret = mod.change_root_pw(passwd)
        if ret == 0:
            print("root password changed")
        else:
            print("failed to change root password")
        return ret

    @staticmethod
    def genpw():
        """
        Returns a random password.
        """
        import string
        chars = string.letters + string.digits + r'+/'
        assert 256 % len(chars) == 0
        pwd_len = 16
        return ''.join(chars[ord(c) % len(chars)] for c in os.urandom(pwd_len))

    def scanscsi(self):
        """
        Rescans the scsi host buses for new logical units discovery.
        """
        try:
            mod = __import__("rcDiskInfo"+rcEnv.sysname)
        except ImportError:
            print("scanscsi is not supported on", rcEnv.sysname, file=sys.stderr)
            return 1
        diskinfo = mod.diskInfo()
        if not hasattr(diskinfo, 'scanscsi'):
            print("scanscsi is not implemented on", rcEnv.sysname, file=sys.stderr)
            return 1
        return diskinfo.scanscsi(
            hba=self.options.hba,
            target=self.options.target,
            lun=self.options.lun,
        )

    def discover(self):
        """
        Auto configures services wrapping cloud compute instances
        """
        self.cloud_init()

    def cloud_init(self):
        """
        Initializes a cloud object for each cloud seaction in the configuration
        file.
        """
        ret = 0
        for section in self.config.sections():
            try:
                self.cloud_init_section(section)
            except ex.excInitError as exc:
                print(str(exc), file=sys.stderr)
                ret |= 1
        return ret

    def cloud_get(self, section):
        """
        Get the cloud object instance handling the config file section passed
        as argument. If not already instanciated, create the instance and
        store it in a dict hashed by section name.
        """
        if not section.startswith("cloud"):
            return

        if not section.startswith("cloud#"):
            raise ex.excInitError("cloud sections must have a unique name in "
                                  "the form '[cloud#n] in %s" % rcEnv.paths.nodeconf)

        if self.clouds and section in self.clouds:
            return self.clouds[section]

        if not self.config.has_option(section, "type"):
            raise ex.excInitError("type option is mandatory in cloud section "
                                  "in %s" % rcEnv.paths.nodeconf)
        cloud_type = self.config.get(section, 'type')

        if len(cloud_type) == 0:
            raise ex.excInitError("invalid cloud type in %s"%rcEnv.paths.nodeconf)

        self.load_auth_config()
        if not self.auth_config.has_section(section):
            raise ex.excInitError("%s must have a '%s' section" % (rcEnv.paths.authconf, section))

        auth_dict = {}
        for key, val in self.auth_config.items(section):
            auth_dict[key] = val

        mod_name = "rcCloud" + cloud_type[0].upper() + cloud_type[1:].lower()

        try:
            mod = __import__(mod_name)
        except ImportError:
            raise ex.excInitError("cloud type '%s' is not supported"%cloud_type)

        if self.clouds is None:
            self.clouds = {}
        cloud = mod.Cloud(section, auth_dict)
        self.clouds[section] = cloud
        return cloud

    def cloud_init_section(self, section):
        """
        Detects all cloud instances in the section, and init a service for each
        """
        cloud = self.cloud_get(section)

        if cloud is None:
            return

        cloud_id = cloud.cloud_id()
        svcnames = cloud.list_svcnames()

        self.cloud_purge_services(cloud_id, [x[1] for x in svcnames])

        for vmname, svcname in svcnames:
            self.cloud_init_service(cloud, vmname, svcname)

    @staticmethod
    def cloud_purge_services(suffix, svcnames):
        """
        Purge a lingering service no longer detected in the cloud.
        """
        import glob
        fpaths = glob.glob(os.path.join(rcEnv.paths.pathetc, '*.conf'))
        for fpath in fpaths:
            svcname = os.path.basename(fpath)[:-5]
            if svcname.endswith(suffix) and svcname not in svcnames:
                print("purge_service(svcname)", svcname)

    @staticmethod
    def cloud_init_service(cloud, vmname, svcname):
        """
        Init a service for a detected cloud instance.
        """
        import glob
        fpaths = glob.glob(os.path.join(rcEnv.paths.pathetc, '*.conf'))
        fpath = os.path.join(rcEnv.paths.pathetc, svcname+'.conf')
        if fpath in fpaths:
            print(svcname, "is already defined")
            return
        print("initialize", svcname)

        defaults = {
            "app": cloud.app_id(svcname),
            "mode": cloud.mode,
            "nodes": rcEnv.nodename,
            "service_type": "TST",
            "vm_name": vmname,
            "cloud_id": cloud.cid,
        }
        config = rcConfigParser.RawConfigParser(defaults)

        try:
            ofile = open(fpath, 'w')
            config.write(ofile)
            ofile.close()
        except:
            print("failed to write %s"%fpath, file=sys.stderr)
            raise Exception()

        basename = fpath[:-5]
        launchers_d = basename + '.dir'
        launchers_l = basename + '.d'
        try:
            os.makedirs(launchers_d)
        except OSError:
            pass
        try:
            os.symlink(launchers_d, launchers_l)
        except OSError:
            pass
        try:
            os.symlink(rcEnv.paths.svcmgr, basename)
        except OSError:
            pass

    def can_parallel(self, action, options):
        """
        Returns True if the action can be run in a subprocess per service
        """
        if options.parallel and action not in ACTIONS_NO_PARALLEL:
            return True
        return False

    @staticmethod
    def action_need_aggregate(action, options):
        """
        Returns True if the action returns data from multiple sources (nodes
        or services) to arrange for display.
        """
        if action.startswith("print_") and options.format == "json":
            return True
        if action.startswith("json_"):
            return True
        if action.startswith("collector_"):
            return True
        if "_json_" in action:
            return True
        return False

    def do_svcs_action(self, action, options):
        """
        The services action wrapper.
        Takes care of
        * parallelization of the action in per-service subprocesses
        * collection and aggregation of returned data and errors
        """
        if action == "ls":
            for svc in self.svcs:
                print(svc.svcname)
            return

        err = 0
        data = Storage()
        data.outs = {}
        need_aggregate = self.action_need_aggregate(action, options)

        # generic cache janitoring
        purge_cache_expired()
        self.log.debug("session uuid: %s", rcEnv.session_uuid)

        if action in ACTIONS_NO_MULTIPLE_SERVICES and len(self.svcs) > 1:
            print("action '%s' is not allowed on multiple services" % action, file=sys.stderr)
            return 1

        if self.can_parallel(action, options):
            from multiprocessing import Process
            if rcEnv.sysname == "Windows":
                from multiprocessing import set_executable
                set_executable(os.path.join(sys.exec_prefix, 'pythonw.exe'))
            data.procs = {}
            data.svcs = {}

        def running():
            count = 0
            for proc in data.procs.values():
                if proc.is_alive():
                    count += 1
            return count

        for svc in self.svcs:
            if self.can_parallel(action, options):
                while running() >= self.max_parallel:
                    time.sleep(1)
                data.svcs[svc.svcname] = svc
                data.procs[svc.svcname] = Process(
                    target=self.service_action_worker,
                    name='worker_'+svc.svcname,
                    args=[svc, action, options],
                )
                data.procs[svc.svcname].start()
            else:
                try:
                    ret = svc.action(action, options)
                    if need_aggregate:
                        if ret is not None:
                            data.outs[svc.svcname] = ret
                    elif action.startswith("print_"):
                        self.print_data(ret)
                        ret = 0
                    else:
                        if ret is None:
                            ret = 0
                        elif isinstance(ret, list):
                            ret = 0
                        elif isinstance(ret, dict):
                            if "error" in ret:
                                print(ret["error"], file=sys.stderr)
                            else:
                                print("unsupported format for this action", file=sys.stderr)
                            ret = 1
                        err += ret
                except ex.excError as exc:
                    if not need_aggregate:
                        print("%s: %s" % (svc.svcname, exc), file=sys.stderr)
                    continue
                except ex.excSignal:
                    break

        if self.can_parallel(action, options):
            for svcname in data.procs:
                data.procs[svcname].join()
                ret = data.procs[svcname].exitcode
                if ret > 0:
                    # r is negative when data.procs[svcname] is killed by signal.
                    # in this case, we don't want to decrement the err counter.
                    err += ret

        if need_aggregate:
            if self.options.single_service:
                svcname = self.svcs[0].svcname
                if svcname not in data.outs:
                    return 1
                self.print_data(data.outs[svcname])
            else:
                self.print_data(data.outs)

        return err

    def collector_cli(self):
        """
        The collector cli entrypoint.
        """
        data = {}

        if self.options.user is None and self.options.config is None and os.getuid() == 0:
            if self.options.user is None:
                user, password = self.collector_auth_node()
                data["user"] = user
                data["password"] = password
                data["save"] = False
            if self.options.api is None:
                if rcEnv.dbopensvc is None:
                    raise ex.excError("node.dbopensvc is not set in node.conf")
                data["api"] = rcEnv.dbopensvc.replace("/feed/default/call/xmlrpc", "/init/rest/api")
        else:
            data["user"] = self.options.user
            data["password"] = self.options.password
            data["api"] = self.options.api
            data["save"] = self.options.save

        data["insecure"] = self.options.insecure
        data["refresh_api"] = self.options.refresh_api
        data["fmt"] = self.options.format
        if self.options.config:
            data["config"] = self.options.config
        argv = [word for word in self.options.extra_argv]
        argv = drop_option("--refresh-api", argv, drop_value=False)
        argv = drop_option("--insecure", argv, drop_value=False)
        argv = drop_option("--save", argv, drop_value=False)
        argv = drop_option("--help", argv, drop_value=False)
        argv = drop_option("--debug", argv, drop_value=False)
        argv = drop_option("--format", argv, drop_value=True)
        argv = drop_option("--color", argv, drop_value=True)
        argv = drop_option("--api", argv, drop_value=True)
        argv = drop_option("--config", argv, drop_value=True)
        argv = drop_option("--user", argv, drop_value=True)
        argv = drop_option("--password", argv, drop_value=True)
        return self._collector_cli(data, argv)

    def _collector_cli(self, data, argv):
        from rcCollectorCli import Cli
        cli = Cli(**data)
        return cli.run(argv=argv)

    def download_from_safe(self, safe_id, svcname=None):
        import base64
        from rcUtilities import bdecode
        if safe_id.startswith("safe://"):
            safe_id = safe_id[7:].lstrip("/")
        fpath = os.path.join(rcEnv.paths.safe, safe_id)
        if os.path.exists(fpath):
            with open(fpath, "r") as ofile:
                buff = ofile.read()
            return base64.urlsafe_b64decode(self.decrypt(buff)[1]["data"])
        path = "/safe/%s/download" % safe_id
        api = self.collector_api(svcname=svcname)
        request = self.collector_request(path)
        if api["url"].startswith("https"):
            try:
                import ssl
                kwargs = {"context": ssl._create_unverified_context()}
            except:
                kwargs = {}
        else:
            raise ex.excError("refuse to submit auth tokens through a non-encrypted transport")
        try:
            f = urlopen(request, **kwargs)
        except HTTPError as e:
            try:
                err = json.loads(e.read())["error"]
                e = ex.excError(err)
            except ValueError:
                pass
            raise e
        buff = b""
        for chunk in iter(lambda: f.read(4096), b""):
            buff += chunk
        data = {"data": bdecode(base64.urlsafe_b64encode(buff))}
        if not os.path.exists(rcEnv.paths.safe):
            os.makedirs(rcEnv.paths.safe)
        with open(fpath, 'w') as df:
            pass
        os.chmod(fpath, 0o0600)
        with open(fpath, 'w') as df:
            df.write(self.encrypt(data, encode=False))
        f.close()
        return buff

    def collector_api(self, svcname=None):
        """
        Prepare the authentication info, either as node or as user.
        Fetch and cache the collector's exposed rest api metadata.
        """
        if rcEnv.dbopensvc is None:
            raise ex.excError("node.dbopensvc is not set in node.conf")
        data = {}
        if self.options.user is None:
            username, password = self.collector_auth_node()
            if svcname:
                username = svcname+"@"+username
        else:
            username, password = self.collector_auth_user()
        data["username"] = username
        data["password"] = password
        data["url"] = rcEnv.dbopensvc.replace("/feed/default/call/xmlrpc", "/init/rest/api")
        return data

    def collector_auth_node(self):
        """
        Returns the authentcation info for login as node
        """
        username = rcEnv.nodename
        if not self.config.has_option("node", "uuid"):
            raise ex.excError("the node is not registered yet. use 'nodemgr register [--user <user>]'")
        password = self.config.get("node", "uuid")
        return username, password

    def collector_auth_user(self):
        """
        Returns the authentcation info for login as user
        """
        username = self.options.user

        if self.options.password and self.options.password != "?":
            return username, self.options.password

        import getpass
        try:
            password = getpass.getpass()
        except EOFError:
            raise KeyboardInterrupt()
        return username, password

    def collector_request(self, path, svcname=None):
        """
        Make a request to the collector's rest api
        """
        import base64
        api = self.collector_api(svcname=svcname)
        url = api["url"]
        if not url.startswith("https"):
            raise ex.excError("refuse to submit auth tokens through a "
                              "non-encrypted transport")
        request = Request(url+path)
        auth_string = '%s:%s' % (api["username"], api["password"])
        if sys.version_info[0] >= 3:
            base64string = base64.encodestring(auth_string.encode()).decode()
        else:
            base64string = base64.encodestring(auth_string)
        base64string = base64string.replace('\n', '')
        request.add_header("Authorization", "Basic %s" % base64string)
        return request

    def collector_rest_get(self, path, data=None, svcname=None):
        """
        Make a GET request to the collector's rest api
        """
        return self.collector_rest_request(path, data=data, svcname=svcname)

    def collector_rest_post(self, path, data=None, svcname=None):
        """
        Make a POST request to the collector's rest api
        """
        return self.collector_rest_request(path, data, svcname=svcname, get_method="POST")

    def collector_rest_put(self, path, data=None, svcname=None):
        """
        Make a PUT request to the collector's rest api
        """
        return self.collector_rest_request(path, data, svcname=svcname, get_method="PUT")

    def collector_rest_delete(self, path, data=None, svcname=None):
        """
        Make a DELETE request to the collector's rest api
        """
        return self.collector_rest_request(path, data, svcname=svcname, get_method="DELETE")

    @staticmethod
    def set_ssl_context(kwargs):
        """
        Python 2.7.9+ verifies certs by default and support the creationn
        of an unverified context through ssl._create_unverified_context().
        This method add an unverified context to a kwargs dict, when
        necessary.
        """
        try:
            import ssl
            kwargs["context"] = ssl._create_unverified_context()
        except (ImportError, AttributeError):
            pass
        return kwargs

    def urlretrieve(self, url, fpath):
        """
        A chunked download method
        """
        request = Request(url)
        kwargs = {}
        kwargs = self.set_ssl_context(kwargs)
        ufile = urlopen(request, **kwargs)
        with open(fpath, 'wb') as ofile:
            for chunk in iter(lambda: ufile.read(4096), b""):
                ofile.write(chunk)
        ufile.close()

    def collector_rest_request(self, path, data=None, svcname=None, get_method="GET"):
        """
        Make a request to the collector's rest api
        """
        if data is not None and get_method == "GET":
            if len(data) == 0 or not isinstance(data, dict):
                data = None
            else:
                path += "?" + urlencode(data)
                data = None

        request = self.collector_request(path, svcname=svcname)
        if get_method:
            request.get_method = lambda: get_method
        if data is not None:
            try:
                request.add_data(urlencode(data))
            except AttributeError:
                request.data = urlencode(data).encode('utf-8')
        kwargs = {}
        kwargs = self.set_ssl_context(kwargs)
        try:
            ufile = urlopen(request, **kwargs)
        except HTTPError as exc:
            try:
                err = json.loads(exc.read())["error"]
                exc = ex.excError(err)
            except (ValueError, TypeError):
                pass
            raise exc
        except IOError as exc:
            if hasattr(exc, "reason"):
                raise ex.excError(exc.reason)
            raise ex.excError(str(exc))
        data = json.loads(ufile.read().decode("utf-8"))
        ufile.close()
        return data

    def collector_rest_get_to_file(self, path, fpath):
        """
        Download bulk chunked data from the collector's rest api
        """
        request = self.collector_request(path)
        kwargs = {}
        kwargs = self.set_ssl_context(kwargs)
        try:
            ufile = urlopen(request, **kwargs)
        except HTTPError as exc:
            try:
                err = json.loads(exc.read())["error"]
                exc = ex.excError(err)
            except ValueError:
                pass
            raise exc
        with open(fpath, 'wb') as ofile:
            for chunk in iter(lambda: ufile.read(4096), b""):
                ofile.write(chunk)
        ufile.close()

    def install_svc_conf_from_templ(self, svcname, template):
        """
        Download a provisioning template from the collector's rest api,
        and installs it as the service configuration file.
        """
        fpath = os.path.join(rcEnv.paths.pathetc, svcname+'.conf')
        try:
            int(template)
            url = "/provisioning_templates/"+str(template)+"?props=tpl_definition&meta=0"
        except ValueError:
            url = "/provisioning_templates?filters=tpl_name="+template+"&props=tpl_definition&meta=0"
        data = self.collector_rest_get(url)
        if "error" in data:
            raise ex.excError(data["error"])
        if len(data["data"]) == 0:
            raise ex.excError("service not found on the collector")
        if len(data["data"][0]["tpl_definition"]) == 0:
            raise ex.excError("service has an empty configuration")
        with open(fpath, "w") as ofile:
            ofile.write(data["data"][0]["tpl_definition"].replace("\\n", "\n").replace("\\t", "\t"))
        self.install_svc_conf_from_file(svcname, fpath)

    def install_svc_conf_from_uri(self, svcname, fpath):
        """
        Download a provisioning template from an arbitrary uri,
        and installs it as the service configuration file.
        """
        import tempfile
        tmpf = tempfile.NamedTemporaryFile()
        tmpfpath = tmpf.name
        tmpf.close()
        print("get %s (%s)" % (fpath, tmpfpath))
        try:
            self.urlretrieve(fpath, tmpfpath)
        except IOError as exc:
            print("download failed", ":", exc, file=sys.stderr)
            try:
                os.unlink(tmpfpath)
            except OSError:
                pass
        self.install_svc_conf_from_file(svcname, tmpfpath)

    @staticmethod
    def install_svc_conf_from_file(svcname, fpath):
        """
        Installs a local template as the service configuration file.
        """
        if not os.path.exists(fpath):
            raise ex.excError("%s does not exists" % fpath)

        import shutil

        # install the configuration file in etc/
        src_cf = os.path.realpath(fpath)
        dst_cf = os.path.join(rcEnv.paths.pathetc, svcname+'.conf')
        if dst_cf != src_cf:
            shutil.copy2(src_cf, dst_cf)

    @staticmethod
    def install_svc_conf_from_data(svcname, data):
        """
        Installs a service configuration file from section, keys and values
        fed from a data structure.
        """
        fpath = os.path.join(rcEnv.paths.pathetc, svcname+'.conf')
        config = rcConfigParser.RawConfigParser()

        for section_name, section in data.items():
            if section_name != "DEFAULT":
                config.add_section(section_name)
            for key, value in section.items():
                config.set(section_name, key, value)
        try:
            with open(fpath, 'w') as ofile:
                config.write(ofile)
        except:
            print("failed to write %s"%fpath, file=sys.stderr)
            raise Exception()

    def install_service(self, svcname, fpath=None, template=None):
        """
        Pick a collector's template, arbitrary uri, or local file service
        configuration file fetching method. Run it, and create the
        service symlinks and launchers directory.
        """
        if isinstance(svcname, list):
            if len(svcname) != 1:
                raise ex.excError("only one service must be specified")
            svcname = svcname[0]

        data = None
        if sys.stdin and not sys.stdin.isatty():
            feed = ""
            for line in sys.stdin.readlines():
                feed += line
            if feed:
                try:
                    data = json.loads("".join(feed))
                except ValueError:
                    raise ex.excError("invalid json feed")

        if fpath is not None and template is not None:
            raise ex.excError("--config and --template can't both be specified")

        # freeze before the installing the config so the daemon never
        # has a chance to consider the new service unfrozen and take undue
        # action before we have the change to modify the service config
        Freezer(svcname).freeze()

        ret = 0
        if data is not None:
            self.install_svc_conf_from_data(svcname, data)
        elif template is not None:
            if "://" in template:
                self.install_svc_conf_from_uri(svcname, template)
            elif os.path.exists(template):
                self.install_svc_conf_from_file(svcname, template)
            else:
                self.install_svc_conf_from_templ(svcname, template)
        elif fpath is not None:
            if "://" in fpath:
                self.install_svc_conf_from_uri(svcname, fpath)
            else:
                self.install_svc_conf_from_file(svcname, fpath)
        else:
            ret = 2

        self.install_service_files(svcname)
        self.wake_monitor()
        return ret

    @staticmethod
    def install_service_files(svcname):
        """
        Given a service name, install the symlink to svcmgr.
        """
        if rcEnv.sysname == 'Windows':
            return

        # install svcmgr link
        svcmgr_l = os.path.join(rcEnv.paths.pathetc, svcname)
        if not os.path.exists(svcmgr_l):
            os.symlink(rcEnv.paths.svcmgr, svcmgr_l)
        elif os.path.realpath(rcEnv.paths.svcmgr) != os.path.realpath(svcmgr_l):
            os.unlink(svcmgr_l)
            os.symlink(rcEnv.paths.svcmgr, svcmgr_l)

    def set_rlimit(self):
        """
        Set the operating system nofile rlimit to a sensible value for the
        number of services configured.
        """
        nofile = 4096
        if self.svcs and isinstance(self.svcs, list):
            proportional_nofile = 64 * len(self.svcs)
            if proportional_nofile > nofile:
                nofile = proportional_nofile

        try:
            import resource
            _vs, _vg = resource.getrlimit(resource.RLIMIT_NOFILE)
            if _vs < nofile:
                self.log.debug("raise nofile resource from %d limit to %d", _vs, nofile)
                if nofile > _vg:
                    _vg = nofile
                resource.setrlimit(resource.RLIMIT_NOFILE, (nofile, _vg))
            else:
                self.log.debug("current nofile %d already over minimum %d", _vs, nofile)
        except Exception as exc:
            self.log.debug(str(exc))

    def events(self, nodename=None):
        try:
            self._events(nodename=nodename)
        except ex.excSignal:
            return
        except (OSError, IOError) as exc:
            if exc.errno == 32:
                # broken pipe
                return

    def _events(self, nodename=None):
        if self.options.node:
            nodename = self.options.node
        elif nodename is None:
            nodename = rcEnv.nodename
        from rcColor import format_json
        for msg in self.daemon_events(nodename):
            print(msg)

    def logs(self, nodename=None):
        try:
            self._logs(nodename=nodename)
        except ex.excSignal:
            return
        except (OSError, IOError) as exc:
            if exc.errno == 32:
                # broken pipe
                return

    def _logs(self, nodename=None):
        if nodename is None:
            nodename = self.options.node
        if self.options.local:
            nodes = [rcEnv.nodename]
        elif self.options.node:
            nodes = [self.options.node]
        else:
            nodes = self.cluster_nodes
        from rcColor import colorize_log_line
        lines = []
        for nodename in nodes:
            lines += self.daemon_backlogs(nodename)
            for line in sorted(lines):
                line = colorize_log_line(line)
                if line:
                    print(line)
        if not self.options.follow:
            return
        for line in self.daemon_logs(nodes):
            line = colorize_log_line(line)
            if line:
                print(line)


    @formatter
    def print_config_data(self, src_config=None):
        return ExtConfig.print_config_data(self, src_config=src_config)

    def print_devs(self):
        if self.options.reverse:
            self.devtree.print_tree_bottom_up(devices=self.options.devices,
                                              verbose=self.options.verbose)
        else:
            self.devtree.print_tree(devices=self.options.devices,
                                    verbose=self.options.verbose)

    def print_config(self):
        """
        print_config node action entrypoint
        """
        if self.options.format is not None:
            return self.print_config_data(self.config)
        if not os.path.exists(rcEnv.paths.nodeconf):
            return
        from rcColor import print_color_config
        print_color_config(rcEnv.paths.nodeconf)

    def print_authconfig(self):
        """
        print_authconfig node action entrypoint
        """
        if self.options.format is not None:
            self.load_auth_config()
            return self.print_config_data(self.auth_config)
        if not os.path.exists(rcEnv.paths.authconf):
            return
        from rcColor import print_color_config
        print_color_config(rcEnv.paths.authconf)

    def agent_version(self):
        try:
            import version
        except ImportError:
            pass

        try:
            reload(version)
            return version.version
        except:
            pass

        try:
            import imp
            imp.reload(version)
            return version.version
        except:
            pass

        try:
            import importlib
            importlib.reload(version)
            return version.version
        except:
            pass
        if which("git"):
            cmd = ["git", "--git-dir", os.path.join(rcEnv.paths.pathsvc, ".git"),
                   "describe", "--tags", "--abbrev=0"]
            out, err, ret = justcall(cmd)
            if ret != 0:
                return "dev"
            _version = out.strip()
            cmd = ["git", "--git-dir", os.path.join(rcEnv.paths.pathsvc, ".git"),
                   "describe", "--tags"]
            out, err, ret = justcall(cmd)
            if ret != 0:
                return "dev"
            _release = out.strip().split("-")[1]
            return "-".join((_version, _release+"dev"))

        return "dev"

    def frozen(self):
        """
        Return True if the node frozen flag is set.
        """
        return self.freezer.node_frozen()

    def freeze(self):
        """
        Set the node frozen flag.
        """
        self.freezer.node_freeze()

    def thaw(self):
        """
        Unset the node frozen flag.
        """
        self.freezer.node_thaw()

    def stonith(self):
        self._stonith(self.options.node)

    def _stonith(self, node):
        if node in (None, ""):
            raise ex.excError("--node is mandatory")
        if node == rcEnv.nodename:
            raise ex.excError("refuse to stonith ourself")
        if node not in self.cluster_nodes:
            raise ex.excError("refuse to stonith node %s not member of our cluster" % node)
        try:
            cmd = self._get("stonith#%s.cmd" % node)
        except ex.excError as exc:
            raise ex.excError("the stonith#%s.cmd keyword must be set in "
                              "node.conf" % node)
        import shlex
        cmd = shlex.split(cmd)
        self.log.info("stonith node %s", node)
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError()

    def prepare_async_cmd(self):
        cmd = sys.argv[1:]
        cmd = drop_option("--node", cmd, drop_value=True)
        return cmd

    def async_action(self, action, timeout=None, wait=None):
        """
        Set the daemon global expected state if the action can be handled
        by the daemons.
        """
        if wait is None:
            wait = self.options.wait
        if timeout is None:
            timeout = self.options.time
        if timeout is not None:
            timeout = convert_duration(timeout)
        if self.options.node is not None and self.options.node != "" and \
           action in REMOTE_ACTIONS:
            cmd = self.prepare_async_cmd()
            sync = action not in ("reboot", "shutdown", "updatepkg")
            ret = self.daemon_node_action(cmd, sync=sync)
            if ret == 0:
                raise ex.excAbortAction()
            else:
                raise ex.excError()
        states = {
            "freeze": "frozen",
            "thaw": "thawed",
        }
        if self.options.local:
            return
        if action not in states:
            return
        self.set_node_monitor(global_expect=states[action])
        self.log.info("%s action requested", action)
        if not wait:
            raise ex.excAbortAction()
        self.poll_async_action(states[action], timeout=timeout)

    def poll_async_action(self, state, timeout=None):
        """
        Display an asynchronous action progress until its end or timeout
        """
        prev_global_expect_set = set()

        for _ in range(timeout):
            data = self._daemon_status()
            if data is None:
                raise ex.excError("the daemon is not running")
            global_expect_set = []
            for nodename in data["monitor"]["nodes"]:
                try:
                    _data = data["monitor"]["nodes"][nodename]
                except (KeyError, TypeError) as exc:
                    continue
                if _data["monitor"].get("global_expect") == state:
                    global_expect_set.append(nodename)
            if prev_global_expect_set != global_expect_set:
                for nodename in set(global_expect_set) - prev_global_expect_set:
                    self.log.info(" work starting on %s", nodename)
                for nodename in prev_global_expect_set - set(global_expect_set):
                    self.log.info(" work over on %s", nodename)
            if not global_expect_set:
                self.log.info("final status: frozen=%s",
                              data["monitor"]["frozen"])
                return
            prev_global_expect_set = set(global_expect_set)
            time.sleep(1)
        raise ex.excError("wait timeout exceeded")

    #
    # daemon actions
    #
    def daemon_collector_xmlrpc(self, *args, **kwargs):
        data = self.daemon_send(
            {
                "action": "collector_xmlrpc",
                "options": {
                    "args": args,
                    "kwargs": kwargs,
                },
            },
            nodename=self.options.node,
            silent=True,
        )
        return data

    def _daemon_status(self, silent=False, refresh=False, node=None):
        data = self.daemon_send(
            {
                "action": "daemon_status",
                "options": {
                    "refresh": refresh,
                },
            },
            nodename=node,
            silent=silent,
        )
        return data

    def daemon_status(self, svcnames=None, preamble="", node=None):
        if node:
            daemon_node = node
        elif self.options.node:
            daemon_node = self.options.node
        else:
            daemon_node = rcEnv.nodename
        data = self._daemon_status(node=daemon_node)
        if data is None or data.get("status", 0) != 0:
            return

        from converters import print_size
        from rcColor import format_json, colorize, color, unicons
        if self.options.format == "json":
            format_json(data)
            return

        from rcStatus import Status, colorize_status
        out = []

        def get_nodes():
            try:
                hb_id = [thr_id for thr_id in data.keys() if thr_id.startswith("hb#")][0]
                hb = data[hb_id]
                return sorted(hb["peers"].keys())
            except:
                return [rcEnv.nodename]

        self.build_services(minimal=True)
        nodenames = get_nodes()
        services = {}

        def load_header(title):
            line = [
                title,
                "",
                "",
                "",
            ]
            for nodename in nodenames:
                line.append(colorize(nodename, color.BOLD))
            out.append(line)

        def load_svc(svcname, prefix=""):
            if svcname not in services:
                return
            data = services[svcname]
            if svcname in slave_parents and prefix == "":
                return
            try:
                topology = services[svcname].topology
            except KeyError:
                topology = ""
            try:
                nodes = self.services[svcname].nodes
                drpnodes = self.services[svcname].drpnodes
            except KeyError:
                nodes = set()
                drpnodes = set()
            if rcEnv.nodename in drpnodes:
                topology = "drp " + topology
            status = colorize_status(data["avail"], lpad=0)
            if data["overall"] == "warn":
                status += colorize("!", color.BROWN)
            if data["placement"] == "non-optimal":
                status += colorize("^", color.RED)
            if data.get("scale") is not None:
                info = "scaler/%d/%d" % (data["n_up"], data.get("scale"))
            else:
                info = topology
                if data["orchestrate"]:
                    info += "/" + data["orchestrate"]
                if topology == "flex":
                    info += "/%d/%d" % (data["flex_min_nodes"], data["flex_max_nodes"])
            line = [
                " "+colorize(prefix+svcname, color.BOLD),
                status,
                info,
                "|",
            ]
            for nodename in nodenames:
                if nodename in data["nodes"]:
                    val = []
                    # frozen unicon
                    if data["nodes"][nodename]["frozen"]:
                        frozen_icon = colorize(unicons["frozen"], color.BLUE)
                    else:
                        frozen_icon = ""
                    # avail status unicon
                    avail = data["nodes"][nodename]["avail"]
                    if avail == "unknown":
                        avail_icon = colorize("?", color.RED)
                    else:
                        avail_icon = colorize_status(avail, lpad=0).replace(avail, unicons[avail])
                    # overall status unicon
                    overall = data["nodes"][nodename]["overall"]
                    if overall == "warn":
                        overall_icon = colorize_status(overall, lpad=0).replace(overall, unicons[overall])
                    else:
                        overall_icon = ""
                    # mon status
                    smon = data["nodes"][nodename]["mon"]
                    if smon == "idle":
                        # don't display 'idle', as its to normal status and thus repeated as nauseam
                        smon = ""
                    else:
                        smon = " " + smon
                    # global expect
                    if smon == "":
                        global_expect = data["nodes"][nodename]["global_expect"]
                        if global_expect:
                            global_expect = colorize(" >" + str(global_expect), color.LIGHTBLUE)
                        else:
                            global_expect = ""
                    else:
                        global_expect = ""
                    # leader
                    if data["nodes"][nodename]["placement"] == "leader":
                        leader = colorize("^", color.LIGHTBLUE)
                    else:
                        leader = ""
                    # provisioned
                    if data["nodes"][nodename]["provisioned"] is False:
                        provisioned = colorize("P", color.RED)
                    else:
                        provisioned = ""

                    val.append(avail_icon)
                    val.append(overall_icon)
                    val.append(leader)
                    val.append(frozen_icon)
                    val.append(provisioned)
                    val.append(smon)
                    val.append(global_expect)
                    line.append("".join(val))
                elif nodename not in nodes | drpnodes:
                    line.append("")
                else:
                    line.append(colorize("?", color.RED))
            out.append(line)

            from distutils.version import LooseVersion
            for child in sorted(list(data.get("slaves", [])), key=LooseVersion):
                load_svc(child, prefix=prefix+" ")

        def load_hb(key, _data):
            if _data["state"] == "running":
                state = colorize(_data["state"], color.GREEN)
            else:
                state = colorize(_data["state"], color.RED)
            if "addr" in _data["config"] and "port" in _data["config"]:
                config = _data["config"]["addr"]+":"+str(_data["config"]["port"])
            elif "dev" in _data["config"]:
                if _data["config"]["dev"]:
                    config = os.path.basename(_data["config"]["dev"])
                else:
                    config = ""
            elif "relay" in _data["config"]:
                config = _data["config"]["relay"]
            else:
                config = ""
            line = [
                " "+colorize(key, color.BOLD),
                state,
                config,
                "|",
            ]
            for nodename in nodenames:
                if nodename not in _data["peers"] or "beating" not in _data["peers"][nodename]:
                    status = "n/a"
                elif _data["peers"][nodename]["beating"]:
                    status = "up"
                else:
                    status = "down"
                status = colorize_status(status, lpad=0).replace(status, unicons[status])
                line.append(status)
            out.append(line)

        def load_monitor(key, _data):
            if _data["state"] == "running":
                state = colorize(_data["state"], color.GREEN)
            else:
                state = colorize(_data["state"], color.RED)
            transitions = _data.get("transitions", 0)
            if transitions:
                status = "%d transition" % transitions
            else:
                status = ""
            out.append((
                " "+colorize(key, color.BOLD),
                state,
                status,
            ))

        def load_listener(key, _data):
            if _data["state"] == "running":
                state = colorize(_data["state"], color.GREEN)
            else:
                state = colorize(_data["state"], color.RED)
            out.append((
                " "+colorize(key, color.BOLD),
                state,
                _data["config"]["addr"]+":"+str(_data["config"]["port"]),
            ))

        def load_scheduler(key, _data):
            if _data["state"] == "running":
                state = colorize(_data["state"], color.GREEN)
            else:
                state = colorize(_data["state"], color.RED)
            status = ""
            if _data.get("running"):
                status += "%d run " % _data.get("running")
            delayed = len(_data.get("delayed", []))
            if delayed:
                status += "%d wait" % delayed
            out.append((
                " "+colorize(key, color.BOLD),
                state,
                status,
            ))

        def load_thread(key, _data):
            if _data["state"] == "running":
                state = colorize(_data["state"], color.GREEN)
            else:
                state = colorize(_data["state"], color.RED)
            out.append((
                " "+colorize(key, color.BOLD),
                state,
            ))

        if sys.version_info[0] < 3:
            pad = " "
            def print_bytes(val):
                print(val)
            def bare_len(val):
                import re
                ansi_escape = re.compile(r'\x1b[^m]*m')
                val = ansi_escape.sub('', val)
                val = bytes(val).decode("utf-8")
                return len(val)
        else:
            pad = b" "
            def print_bytes(val):
                print(val.decode("utf-8"))
            def bare_len(val):
                import re
                ansi_escape = re.compile(b'\x1b[^m]*m')
                val = ansi_escape.sub(b'', val)
                val = bytes(val).decode("utf-8")
                return len(val)

        def list_print(data):
            if len(data) == 0:
                return
            widths = [0] * len(data[0])
            _data = []
            for line in data:
                _data.append(tuple(map(lambda x: x.encode("utf-8") if x is not None else "".encode("utf-8"), line)))
            for line in _data:
                for i, val in enumerate(line):
                    strlen = bare_len(val)
                    if strlen > widths[i]:
                        widths[i] = strlen
            for line in _data:
                _line = []
                for i, val in enumerate(line):
                    val = val + pad*(widths[i]-bare_len(val))
                    _line.append(val)
                _line = pad.join(_line)
                print_bytes(_line)

        def print_section(data):
            if len(data) == 0:
                return
            list_print(data)

        def load_threads():
            for key in sorted([key for key in data if key != "cluster"]):
                if key.startswith("hb#"):
                    load_hb(key, data[key])
                elif key == "monitor":
                    load_monitor(key, data[key])
                elif key == "scheduler":
                    load_scheduler(key, data[key])
                elif key == "listener":
                    load_listener(key, data[key])
                else:
                    load_thread(key, data[key])

        def load_metrics():
            load_score()
            load_loadavg()
            load_free_total("mem")
            load_free_total("swap")

        def load_score():
            if "monitor" not in data:
                return
            line = [
                colorize(" score", color.BOLD),
                "",
                "",
                "|",
            ]
            for nodename in nodenames:
                line.append(str(data["monitor"]["nodes"].get(nodename, {}).get("stats", {}).get("score", "")))
            out.append(line)

        def load_loadavg():
            if "monitor" not in data:
                return
            line = [
                colorize("  load 15m", color.BOLD),
                "",
                "",
                "|",
            ]
            for nodename in nodenames:
                line.append(str(data["monitor"]["nodes"].get(nodename, {}).get("stats", {}).get("load_15m", "")))
            out.append(line)

        def load_free_total(key):
            if "monitor" not in data:
                return
            line = [
                colorize("  "+key, color.BOLD),
                "",
                "",
                "|",
            ]
            for nodename in nodenames:
                avail = data["monitor"]["nodes"].get(nodename, {}).get("stats", {}).get(key+"_avail")
                total = data["monitor"]["nodes"].get(nodename, {}).get("stats", {}).get(key+"_total")
                limit = 100 - data["monitor"]["nodes"].get(nodename, {}).get("min_avail_"+key, 0)
                if avail is None or total is None:
                    line.append("")
                    continue
                usage = 100 - avail
                total = print_size(total, unit="MB", compact=True)
                if limit:
                    cell = "%d/%d%%:%s" % (usage, limit, total)
                else:
                    cell = "%d%%:%s" % (usage, total)
                if usage > limit:
                    cell = colorize(cell, color.RED)
                line.append(cell)
            out.append(line)

        def load_node_state():
            if "monitor" not in data:
                return
            line = [
                colorize(" state", color.BOLD),
                "",
                "",
                "|",
            ]
            for nodename in nodenames:
                nmon_state = data["monitor"]["nodes"].get(nodename, {}).get("monitor", {}).get("status", "")
                if nmon_state == "idle":
                    nmon_state = ""
                if data["monitor"]["nodes"].get(nodename, {}).get("frozen", ""):
                    frozen = frozen_icon = colorize(unicons["frozen"], color.BLUE)
                else:
                    frozen = ""
                line.append(str(nmon_state)+frozen)
            out.append(line)

        def load_node_compat():
            if "monitor" not in data:
                return
            if data["monitor"].get("compat") is True:
                # no need to clutter if the situation is normal
                return
            line = [
                colorize(" compat", color.BOLD),
                colorize("warn", color.BROWN),
                "",
                "|",
            ]
            for nodename in nodenames:
                compat = data["monitor"]["nodes"].get(nodename, {}).get("compat", "")
                line.append(str(compat))
            out.append(line)

        # init the services hash
        slave_parents = {}
        if "monitor" in data:
            for node in nodenames:
                if node not in data["monitor"]["nodes"]:
                    continue
                try:
                    node_svc_status = data["monitor"]["nodes"][node]["services"]["status"]
                except KeyError:
                    continue
                for svcname, _data in node_svc_status.items():
                    if svcnames and svcname not in svcnames:
                        continue
                    if svcname not in services:
                        services[svcname] = Storage({
                            "topology": _data.get("topology", ""),
                            "orchestrate": _data.get("orchestrate"),
                            "flex_min_nodes": _data.get("flex_min_nodes"),
                            "flex_max_nodes": _data.get("flex_max_nodes"),
                            "scale": _data.get("scale"),
                            "avail": "undef",
                            "overall": "",
                            "nodes": {},
                            "slaves": set(),
                            "n_up": 0,
                        })
                    slaves = _data.get("slaves", [])
                    scale = _data.get("scale")
                    if scale:
                        for child in data["monitor"]["services"]:
                            if re.match("^[0-9]+."+svcname+"$", child) is None:
                                continue
                            slaves.append(child)
                            if node_svc_status.get(child, {}).get("avail") == "up":
                                services[svcname].n_up += 1
                    for child in slaves:
                        if child not in slave_parents:
                            slave_parents[child] = set([svcname])
                        else:
                            slave_parents[child] |= set([svcname])
                    services[svcname].nodes[node] = {
                        "avail": _data.get("avail", "undef"),
                        "overall": _data.get("overall", "undef"),
                        "frozen": _data.get("frozen", False),
                        "mon": _data["monitor"].get("status", ""),
                        "global_expect": _data["monitor"].get("global_expect"),
                        "placement": _data["monitor"].get("placement", ""),
                        "provisioned": _data.get("provisioned"),
                    }
                    services[svcname].slaves |= set(slaves)
            for svcname, _data in data["monitor"]["services"].items():
                if svcnames and svcname not in svcnames:
                    continue
                if svcname not in services:
                    services[svcname] = Storage({
                        "avail": "undef",
                        "overall": "",
                        "nodes": {}
                    })
                services[svcname].avail = _data["avail"]
                services[svcname].overall = _data["overall"]
                services[svcname].placement = _data["placement"]

        # load data in lists
        load_header("Threads")
        load_threads()
        out.append([])
        load_header("Nodes")
        load_metrics()
        load_node_compat()
        load_node_state()
        out.append([])
        load_header("Services")
        for svcname in sorted(list(services.keys())):
            load_svc(svcname)

        # print tabulated lists
        print(preamble)
        print_section(out)

    def daemon_blacklist_clear(self):
        """
        Tell the daemon to clear the senders blacklist
        """
        data = self.daemon_send(
            {"action": "daemon_blacklist_clear"},
            nodename=self.options.node,
        )
        print(json.dumps(data, indent=4, sort_keys=True))

    def daemon_blacklist_status(self):
        """
        Show the daemon senders blacklist
        """
        data = self.daemon_send(
            {"action": "daemon_blacklist_status"},
            nodename=self.options.node,
        )
        print(json.dumps(data, indent=4, sort_keys=True))

    def _ping(self, node):
        """
        Fetch the daemon senders blacklist as a ping test, from either
        a peer or an arbitrator node, swiching between secrets as appropriate.
        """
        if node in self.cluster_nodes or node in (rcEnv.nodename, "127.0.0.1"):
            cluster_name = None
            secret = None
        elif node in self.cluster_drpnodes:
            try:
                secret = self.conf_get("cluster", "secret", impersonate=node)
            except ex.OptNotFound:
                raise ex.excError("unable to find the node %(node)s cluster secret. set cluster.secret@drpnodes or cluster.secret@%(node)s" % dict(node=node))
            try:
                cluster_name = self.conf_get("cluster", "name", impersonate=node)
            except ex.OptNotFound:
                raise ex.excError("unable to find the node %(node)s cluster name. set cluster.name@drpnodes or cluster.name@%(node)s" % dict(node=node))
        else:
            secret = None
            for section in self.config.sections():
                if not section.startswith("arbitrator#"):
                    continue
                try:
                    arbitrator = self.config.get(section, "name")
                except Exception:
                    continue
                if arbitrator != node:
                    continue
                try:
                    secret = self.config.get(section, "secret")
                    cluster_name = "join"
                    break
                except Exception:
                    self.log.warning("missing 'secret' in configuration section %s" % section)
                    continue
            if secret is None:
                raise ex.excError("unable to find a secret for node '%s': neither in cluster.nodes, cluster.drpnodes nor arbitrator#*.name" % node)
        data = self.daemon_send(
            {"action": "daemon_blacklist_status"},
            nodename=node,
            cluster_name=cluster_name,
            secret=secret,
        )
        if data is None or "status" not in data or data["status"] != 0:
            return 1
        return 0

    def ping(self):
        try:
            ret = self._ping(self.options.node)
        except ex.excError as exc:
            print(exc)
            ret = 2
        if ret == 0:
            print("%s is alive" % self.options.node)
        elif ret == 1:
            print("%s is not alive" % self.options.node)
        return ret

    def daemon_shutdown(self):
        """
        Tell the daemon to shutdown all local service instances then die.
        """
        data = self.daemon_send(
            {"action": "daemon_shutdown"},
            nodename=self.options.node,
        )
        if data is None:
            raise ex.excError
        print(json.dumps(data, indent=4, sort_keys=True))

    def _daemon_stop(self):
        """
        Tell the daemon to die or stop a specified thread.
        """
        if not self._daemon_running():
            return
        options = {}
        if self.options.thr_id:
            options["thr_id"] = self.options.thr_id
        if os.environ.get("OPENSVC_AGENT_UPGRADE"):
            options["upgrade"] = True
        data = self.daemon_send(
            {"action": "daemon_stop", "options": options},
            nodename=self.options.node,
        )
        while True:
            if not self._daemon_running():
                break
            time.sleep(0.1)
        return data

    def daemon_stop(self):
        data = None
        if self.options.thr_id is None and self.daemon_handled_by_systemd():
            # 'systemctl restart <osvcunit>' when the daemon has been started
            # manually causes a direct 'nodemgr daemon start', which fails,
            # and systemd fallbacks to 'nodemgr daemon stop' and leaves the
            # daemon stopped.
            # Detect this situation and stop the daemon ourselves.
            if self.daemon_active_systemd():
                data = self.daemon_stop_systemd()
            else:
                if self._daemon_running():
                    data = self._daemon_stop()
        elif self._daemon_running():
            data = self._daemon_stop()
        if data is None:
            return
        if data.get("status") == 0:
            return
        raise ex.excError(json.dumps(data, indent=4, sort_keys=True))

    def daemon_start(self):
        if self.options.thr_id:
            return self.daemon_start_thread()
        if self.daemon_handled_by_systemd():
            return self.daemon_start_systemd()
        return os.system(sys.executable+" "+os.path.join(rcEnv.paths.pathlib, "osvcd.py"))

    def daemon_start_thread(self):
        options = {}
        options["thr_id"] = self.options.thr_id
        if os.environ.get("OPENSVC_AGENT_UPGRADE"):
            options["upgrade"] = True
        data = self.daemon_send(
            {"action": "daemon_start", "options": options},
            nodename=self.options.node,
        )
        if data.get("status") == 0:
            return
        raise ex.excError(json.dumps(data, indent=4, sort_keys=True))

    def daemon_running(self):
        if self._daemon_running():
            return
        else:
            raise ex.excError

    def _daemon_running(self):
        if self.options.thr_id:
            data = self._daemon_status()
            if self.options.thr_id not in data:
                return False
            return data[self.options.thr_id].get("state") == "running"
        from lock import lock, unlock
        lockfd = None
        try:
            lockfd = lock(lockfile=rcEnv.paths.daemon_lock, timeout=0, delay=0)
        except:
            return True
        finally:
            unlock(lockfd)
        return False

    def daemon_start_systemd(self):
        """
        Do daemon start through the systemd, so that the daemon is
        service status is correctly reported.
        """
        os.system("systemctl reset-failed opensvc-agent")
        if os.environ.get("OPENSVC_AGENT_UPGRADE"):
            os.system("systemctl set-environment OPENSVC_AGENT_UPGRADE=1")
        os.system("systemctl start opensvc-agent")
        os.system("systemctl unset-environment OPENSVC_AGENT_UPGRADE")

    def daemon_stop_systemd(self):
        """
        Do daemon stop through the systemd, so that the daemon is not restarted
        by systemd, and the systemd service status is correctly reported.
        """
        if os.environ.get("OPENSVC_AGENT_UPGRADE"):
            os.system("systemctl set-environment OPENSVC_AGENT_UPGRADE=1")
        os.system("systemctl stop opensvc-agent")
        os.system("systemctl unset-environment OPENSVC_AGENT_UPGRADE")

    def daemon_restart_systemd(self):
        """
        Do daemon restart through the systemd, so that the daemon pid is updated
        in systemd, and the systemd service status is correctly reported.
        """
        os.system("systemctl restart opensvc-agent")

    def daemon_handled_by_systemd(self):
        """
        Return True if the system has systemd.
        """
        if which("systemctl") is None:
            return False
        if os.environ.get("LOGNAME") is None:
            # do as if we're not handled by systemd if we're already run by
            # systemd
            return False
        return True

    def daemon_active_systemd(self):
        cmd = ["systemctl", "show", "-p" "ActiveState", "opensvc-agent.service"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return False
        if out.split("=")[-1].strip() == "active":
            return True
        return False

    def daemon_restart(self):
        if self.daemon_handled_by_systemd():
            # 'systemctl restart <osvcunit>' when the daemon has been started
            # manually causes a direct 'nodemgr daemon start', which fails,
            # and systemd fallbacks to 'nodemgr daemon stop' and leaves the
            # daemon stopped.
            # Detect this situation and stop the daemon ourselves.
            if not self.daemon_active_systemd():
                if self._daemon_running():
                    self._daemon_stop()
            return self.daemon_restart_systemd()
        if self._daemon_running():
            self._daemon_stop()
        self.daemon_start()

    @lazy
    def sorted_cluster_nodes(self):
        return sorted(self.cluster_nodes)

    def daemon_leave(self):
        cluster_nodes = self.sorted_cluster_nodes
        if len(self.sorted_cluster_nodes) == 0:
            self.log.info("local node is not member of a cluster")
            return
        if rcEnv.nodename in cluster_nodes:
            cluster_nodes.remove(rcEnv.nodename)

        # freeze and remember the initial frozen state
        initially_frozen = self.frozen()
        if not initially_frozen:
            self.freeze()
            self.log.info("freeze local node")
        else:
            self.log.info("local node is already frozen")

        # leave other nodes
        options = {"thr_id": "tx", "wait": True}
        data = self.daemon_send(
            {"action": "daemon_stop", "options": options},
            nodename=rcEnv.nodename,
        )

        errors = 0
        for nodename in cluster_nodes:
            data = self.daemon_send(
                {"action": "leave"},
                nodename=nodename,
            )
            if data is None:
                self.log.error("leave node %s failed", nodename)
                errors += 1
            else:
                self.log.info("leave node %s", nodename)

        # remove obsolete hb configurations
        for section in self.config.sections():
            if section.startswith("hb#") or \
               section.startswith("arbitrator#"):
                self.log.info("remove configuration %s", section)
                self.config.remove_section(section)
            self.config.remove_section("cluster")

        self.write_config()
        self.unset_lazy("cluster_nodes")

        # leave node frozen if initially frozen or we failed joining all nodes
        if initially_frozen:
            self.log.warning("local node is left frozen as it was already before leave")
        elif errors > 0:
            self.log.warning("local node is left frozen due to leave errors")
        else:
            self.thaw()
            self.log.info("thaw local node")

    def daemon_join(self):
        if self.options.secret is None:
            raise ex.excError("--secret must be set")
        if self.options.node is None:
            raise ex.excError("--node must be set")

        # freeze and remember the initial frozen state
        initially_frozen = self.frozen()
        if not initially_frozen:
            self.freeze()
            self.log.info("freeze local node")
        else:
            self.log.info("local node is already frozen")

        if not self.config.has_section("cluster"):
            self.config.add_section("cluster")
        data = self.daemon_send(
            {"action": "join"},
            nodename=self.options.node,
            cluster_name="join",
            secret=self.options.secret,
        )
        if data is None:
            raise ex.excError("join node %s failed" % self.options.node)
        data = data.get("data")
        if data is None:
            raise ex.excError("join failed: no data in response")
        cluster_name = data.get("cluster", {}).get("name")
        if cluster_name is None:
            raise ex.excError("join failed: no cluster.name in response")
        cluster_id = data.get("cluster", {}).get("id")
        if cluster_id is None:
            raise ex.excError("join failed: no cluster.id in response")
        cluster_nodes = data.get("cluster", {}).get("nodes")
        if cluster_nodes is None:
            raise ex.excError("join failed: no cluster.nodes in response")
        if not isinstance(cluster_nodes, list):
            raise ex.excError("join failed: cluster.nodes value is not a list")
        cluster_drpnodes = data.get("cluster", {}).get("drpnodes")
        quorum = data.get("cluster", {}).get("quorum", False)

        peer_env = data.get("node", {}).get("env")
        if peer_env and peer_env != rcEnv.node_env:
            self.log.info("update node.env %s => %s", rcEnv.node_env, peer_env)
            self.config.set("node", "env", peer_env)

        self.config.set("cluster", "name", cluster_name)
        self.config.set("cluster", "id", cluster_id)
        self.config.set("cluster", "nodes", " ".join(cluster_nodes))
        self.config.set("cluster", "secret", self.options.secret)
        if isinstance(cluster_drpnodes, list) and len(cluster_drpnodes) > 0:
            self.config.set("cluster", "drpnodes", " ".join(cluster_drpnodes))
        if quorum:
            self.config.set("cluster", "quorum", "true")
        elif self.config.has_option("cluster", "quorum"):
            self.config.remove_option("cluster", "quorum")

        for section, _data in data.items():
            if section.startswith("hb#"):
                if self.config.has_section(section):
                    self.log.info("update heartbeat %s", section)
                    self.config.remove_section(section)
                else:
                    self.log.info("add heartbeat %s", section)
                self.config.add_section(section)
                for option, value in _data.items():
                    self.config.set(section, option, value)
            elif section.startswith("stonith#"):
                if self.config.has_section(section):
                    self.log.info("update stonith %s", section)
                    self.config.remove_section(section)
                else:
                    self.log.info("add stonith %s", section)
                self.config.add_section(section)
                for option, value in _data.items():
                    self.config.set(section, option, value)
            elif section.startswith("arbitrator#"):
                if self.config.has_section(section):
                    self.log.info("update arbitrator %s", section)
                    self.config.remove_section(section)
                else:
                    self.log.info("add arbitrator %s", section)
                self.config.add_section(section)
                for option, value in _data.items():
                    self.config.set(section, option, value)

        # remove obsolete hb configurations
        for section in self.config.sections():
            if section.startswith("hb#") and section not in data:
                self.log.info("remove heartbeat %s", section)
                self.config.remove_section(section)

        # remove obsolete stonith configurations
        for section in self.config.sections():
            if section.startswith("stonith#") and section not in data:
                self.log.info("remove stonith %s", section)
                self.config.remove_section(section)

        # remove obsolete arbitrator configurations
        for section in self.config.sections():
            if section.startswith("arbitrator#") and section not in data:
                self.log.info("remove arbitrator %s", section)
                self.config.remove_section(section)

        self.write_config()
        self.log.info("join node %s", self.options.node)

        # join other nodes
        errors = 0
        for nodename in cluster_nodes:
            if nodename in (rcEnv.nodename, self.options.node):
                continue
            data = self.daemon_send(
                {"action": "join"},
                nodename=nodename,
                cluster_name="join",
                secret=self.options.secret,
            )
            if data is None:
                self.log.error("join node %s failed", nodename)
                errors += 1
            else:
                self.log.info("join node %s", nodename)

        # leave node frozen if initially frozen or we failed joining all nodes
        if initially_frozen:
            self.log.warning("local node is left frozen as it was already before join")
        elif errors > 0:
            self.log.warning("local node is left frozen due to join errors")
        else:
            self.thaw()
            self.log.info("thaw local node")

    def set_node_monitor(self, status=None, local_expect=None, global_expect=None):
        options = {
            "status": status,
            "local_expect": local_expect,
            "global_expect": global_expect,
        }
        try:
            data = self.daemon_send(
                {"action": "set_node_monitor", "options": options},
                nodename=self.options.node,
                silent=True,
            )
            if data is None:
                raise ex.excError("the daemon is not running")
            if data and data["status"] != 0:
                raise ex.excError("set monitor status failed")
        except Exception as exc:
            raise ex.excError("set monitor status failed: %s" % str(exc))

    def daemon_node_action(self, cmd, nodename=None, sync=True):
        """
        Execute a node action on a peer node.
        If sync is set, wait for the action result.
        """
        if nodename is None:
            nodename = self.options.node
        options = {
            "cmd": cmd,
            "sync": sync,
        }
        self.log.info("request node action '%s' on node %s", " ".join(cmd), nodename)
        try:
            data = self.daemon_send(
                {"action": "node_action", "options": options},
                nodename=nodename,
                silent=True,
            )
        except Exception as exc:
            self.log.error("node action on node %s failed: %s",
                           nodename, exc)
            return 1
        if data is None or data["status"] != 0:
            self.log.error("node action on node %s failed",
                           nodename)
            return 1
        if "data" not in data:
            return 0
        data = data["data"]
        if data.get("out") and len(data["out"]) > 0:
            for line in data["out"].splitlines():
               print(line)
        if data.get("err") and len(data["err"]) > 0:
            for line in data["err"].splitlines():
               print(line, file=sys.stderr)
        return data["ret"]

    def daemon_backlogs(self, nodename):
        options = {
            "debug": self.options.debug,
            "backlog": self.options.backlog,
        }
        for lines in self.daemon_get_stream(
            {"action": "node_logs", "options": options},
            nodename=nodename,
        ):
            if lines is None:
                break
            for line in lines:
                yield line

    def daemon_events(self, nodename=None):
        options = {}
        for msg in self.daemon_get_streams(
            {"action": "events", "options": options},
            nodenames=[nodename],
        ):
            if msg is None:
                break
            yield msg

    def daemon_logs(self, nodes=None):
        options = {
            "debug": self.options.debug,
            "backlog": 0,
        }
        for lines in self.daemon_get_streams(
            {"action": "node_logs", "options": options},
            nodenames=nodes,
        ):
            if lines is None:
                break
            for line in lines:
                yield line

    def wake_monitor(self):
        options = {}
        try:
            data = self.daemon_send(
                {"action": "wake_monitor", "options": options},
                nodename=self.options.node,
                silent=True,
            )
            if data and data["status"] != 0:
                self.log.warning("wake monitor failed")
        except Exception as exc:
            self.log.warning("wake monitor failed: %s", str(exc))


    ##########################################################################
    #
    # Network actions
    #
    ##########################################################################
    def network_data(self):
        import glob
        import re
        nets = {}
        for net in glob.glob("/opt/cni/net.d/*.conf"):
            try:
                with open(net, "r") as ofile:
                    data = json.load(ofile)
            except ValueError:
                continue
            if data.get("type") == "portmap":
                continue
            net = os.path.basename(net)
            net = re.sub(".conf$", "", net)
            nets[net] = data
        return nets

    @formatter
    def network_ls(self):
        nets = self.network_data()
        if self.options.format == "json":
            return nets
        print("\n".join([net for net in nets]))

    @formatter
    def network_show(self):
        nets = self.network_data()
        if self.options.format == "json":
            if self.options.id not in nets:
                return {}
            return nets[self.options.id]
        if self.options.id not in nets:
            return
        from forest import Forest
        from rcColor import color
        tree = Forest()
        tree.load({self.options.id: nets[self.options.id]}, title=rcEnv.nodename)
        print(tree)

    ##########################################################################
    #
    # Node stats
    #
    ##########################################################################
    def score(self, data):
        """
        Higher scoring nodes get best placement ranking.
        """
        score = 100 / max(data.get("load_15m", 1), 1)
        score += 100 + data.get("mem_avail", 0)
        score += 2 * (100 + data.get("swap_avail", 0))
        return int(score // 7)

    def stats_meminfo(self):
        """
        OS-specific implementations
        """
        pass

    def stats(self):
        data = {}
        try:
            data["load_15m"] = round(os.getloadavg()[2], 1)
        except:
            # None < 0 == True
            pass
        meminfo = self.stats_meminfo()
        if meminfo is not None:
            data.update(meminfo)
        data["score"] = self.score(data)
        return data

    def unset_lazy(self, prop):
        """
        Expose the unset_lazy(self, ...) utility function as a method,
        so Node() users don't have to import it from rcUtilities.
        """
        unset_lazy(self, prop)


0707010005c8d0000081b40000000000000000000000015ab21754000000f4000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/nodeDarwin.py  import node

class Node(node.Node):
    def shutdown(self):
        cmd = ["shutdown", "-h", "now"]
        ret, out, err = self.vcall(cmd)

    def _reboot(self):
        cmd = ["shutdown", "-r", "now"]
        ret, out, err = self.vcall(cmd)
0707010005c8d1000081b40000000000000000000000015ab21754000000fc000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/nodeHP-UX.py   import node

class Node(node.Node):
    def shutdown(self):
        cmd = ["shutdown", "-h", "-y", "0"]
        ret, out, err = self.vcall(cmd)

    def _reboot(self):
        cmd = ["shutdown", "-r", "-y", "0"]
        ret, out, err = self.vcall(cmd)
0707010005c8d2000081b40000000000000000000000015ab2175400000461000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/nodeLinux.py   import node

class Node(node.Node):
    def shutdown(self):
        cmd = ["shutdown", "-h"]
        ret, out, err = self.vcall(cmd)

    def _reboot(self):
        cmd = ["reboot"]
        ret, out, err = self.vcall(cmd)

    def stats_meminfo(self):
        """
        Memory sizes are store in MB.
        Avails are percentages.
        """
        raw_data = {}
        data = {}
        with open("/proc/meminfo", "r") as ofile:
            for line in ofile.readlines():
                elem = line.split()
                if len(elem) < 2:
                    continue
                raw_data[elem[0].rstrip(":")] = int(elem[1])
        data["mem_total"] = raw_data["MemTotal"] // 1024
        try:
            data["mem_avail"] = 100 * raw_data["MemAvailable"] // raw_data["MemTotal"]
        except KeyError:
            data["mem_avail"] = 100 * (raw_data["MemFree"] + raw_data["Cached"] + raw_data.get("SReclaimable", 0)) // raw_data["MemTotal"]
        data["swap_total"] = raw_data["SwapTotal"] // 1024
        data["swap_avail"] = 100 * raw_data["SwapFree"] // raw_data["SwapTotal"]
        return data

   0707010005c8d3000081b40000000000000000000000015ab2175400000618000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/nodeSunOS.py   import node
from rcUtilities import justcall

class Node(node.Node):
    def shutdown(self):
        cmd = ["init", "5"]
        ret, out, err = self.vcall(cmd)

    def _reboot(self):
        cmd = ["init", "6"]
        ret, out, err = self.vcall(cmd)

    def stats_meminfo(self):
        """
        Memory sizes are store in MB.
        Avails are percentages.

        # kstat -n system_pages -p 
        unix:0:system_pages:availrmem   48190
        unix:0:system_pages:physmem     257948

        # swap -l 
        swapfile             dev  swaplo blocs   libres
        /dev/dsk/c0d0s1     102,1       8 2425800 2425800
        """
        import mmap

        raw_data = {}
        data = {}
        out, err, ret = justcall(["swap", "-l"])
        swap_avail = 0
        swap_total = 0
        for line in out.splitlines():
            if line[0] != "/":
                continue
            elem = line.split()
            swap_avail += int(elem[-1]) // 2
            swap_total += int(elem[-2]) // 2
        data["swap_avail"] = 100 - swap_avail // swap_total
        data["swap_total"] = swap_total // 1024

        out, err, ret = justcall(["kstat", "-n", "system_pages", "-p"])
        for line in out.splitlines():
            elem = line.split()
            raw_data[elem[0]] = elem[1]

        data["mem_total"] = int(raw_data["unix:0:system_pages:physmem"]) * mmap.PAGESIZE // 1024 // 1024
        data["mem_avail"] = 100 * int(raw_data["unix:0:system_pages:availrmem"]) // int(raw_data["unix:0:system_pages:physmem"])
        return data

0707010005c8d4000081b40000000000000000000000015ab217540000af9e000000660000000000000000000000000000002700000000root/usr/share/opensvc/lib/nodedict.py    import sys
from rcGlobalEnv import rcEnv
from keywords import Keyword, KeywordStore

# deprecated => supported
DEPRECATED_KEYWORDS = {
    "node.host_mode": "env",
    "node.environment": "asset_env",
    "node.environnement": "asset_env",
}

# supported => deprecated
REVERSE_DEPRECATED_KEYWORDS = {
    "node.asset_env": ["environnement", "environment"],
    "node.env": ["host_mode"],
}

DEPRECATED_SECTIONS = {
}

BASE_SECTIONS = [
    "node",
    "cluster",
    "compliance",
    "stats",
    "checks",
    "packages",
    "patches",
    "asset",
    "nsr",
    "dcs",
    "hds",
    "necism",
    "eva",
    "ibmsvc",
    "vioserver",
    "brocade",
    "disks",
    "sym",
    "rotate_root_pw",
    "listener",
    "syslog",
    "stats_collection",
    "reboot",
    "cluster",
]

class KeywordNodeUuid(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="node",
                  keyword="uuid",
                  order=15,
                  text="The auth token provided by the collector on 'nodemgr register'."
                )

class KeywordNodeMinAvailMem(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="node",
                  keyword="min_avail_mem",
                  order=15,
                  default="2%",
                  convert="size",
                  text="The minimum required available memory to allow orchestration."
                )

class KeywordNodeMinAvailSwap(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="node",
                  keyword="min_avail_swap",
                  order=15,
                  default="10%",
                  convert="size",
                  text="The minimum required available swap to allow orchestration."
                )

class KeywordNodeEnv(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="node",
                  keyword="env",
                  order=15,
                  default="TST",
                  candidates=rcEnv.allowed_svc_envs,
                  text="A non-PRD service can not be brought up on a PRD node, but a PRD service can be startup on a non-PRD node (in a DRP situation)."
                )

class KeywordNodeMaxParallel(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="node",
                  keyword="max_parallel",
                  order=15,
                  default=10,
                  convert="integer",
                  text="Allow a maximum of <max_parallel> subprocesses to run simultaneously on 'svcmgr --parallel <action>' commands."
                )

class KeywordNodePrkey(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="node",
                  keyword="prkey",
                  order=15,
                  default_text="<autogenerated>",
                  text="The scsi3 persistent reservation key used by the pr resources."
                )

class KeywordNodeLocCountry(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="node",
                  keyword="loc_country",
                  order=15,
                  example="fr",
                  text="An asset information to push to the collector on pushasset, overriding the currently stored value."
                )

class KeywordNodeLocCity(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="node",
                  keyword="loc_city",
                  order=15,
                  example="Paris",
                  text="An asset information to push to the collector on pushasset, overriding the currently stored value."
                )

class KeywordNodeLocZip(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="node",
                  keyword="loc_zip",
                  order=15,
                  example="75017",
                  text="An asset information to push to the collector on pushasset, overriding the currently stored value."
                )

class KeywordNodeLocAddr(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="node",
                  keyword="loc_addr",
                  order=15,
                  example="7 rue blanche",
                  text="An asset information to push to the collector on pushasset, overriding the currently stored value."
                )

class KeywordNodeLocBuilding(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="node",
                  keyword="loc_building",
                  order=15,
                  example="Crystal",
                  text="An asset information to push to the collector on pushasset, overriding the currently stored value."
                )

class KeywordNodeLocFloor(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="node",
                  keyword="loc_floor",
                  order=15,
                  example="21",
                  text="An asset information to push to the collector on pushasset, overriding the currently stored value."
                )

class KeywordNodeLocRoom(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="node",
                  keyword="loc_room",
                  order=15,
                  example="102",
                  text="An asset information to push to the collector on pushasset, overriding the currently stored value."
                )

class KeywordNodeLocRack(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="node",
                  keyword="loc_rack",
                  order=15,
                  example="R42",
                  text="An asset information to push to the collector on pushasset, overriding the currently stored value."
                )

class KeywordNodeSecZone(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="node",
                  keyword="sec_zone",
                  order=15,
                  example="dmz1",
                  text="An asset information to push to the collector on pushasset, overriding the currently stored value."
                )

class KeywordNodeTeamInteg(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="node",
                  keyword="team_integ",
                  order=15,
                  example="TINT",
                  text="An asset information to push to the collector on pushasset, overriding the currently stored value."
                )

class KeywordNodeTeamSupport(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="node",
                  keyword="team_support",
                  order=15,
                  example="TSUP",
                  text="An asset information to push to the collector on pushasset, overriding the currently stored value."
                )

class KeywordNodeAssetEnv(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="node",
                  keyword="asset_env",
                  order=15,
                  example="Production",
                  text="An asset information to push to the collector on pushasset, overriding the currently stored value."
                )

class KeywordNodeConnectTo(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="node",
                  keyword="connect_to",
                  order=15,
                  example="1.2.3.4",
                  default_text="On GCE instances, defaults to the instance ip address.",
                  text="An asset information to push to the collector on pushasset, overriding the currently stored value."
                )

class KeywordNodeDbopensvc(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="node",
                  keyword="dbopensvc",
                  order=15,
                  example="https://collector.opensvc.com",
                  text="Set the uri of the collector main xmlrpc server. The path part of the uri can be left unspecified. If not set, the agent does not try to communicate with a collector."
                )

class KeywordNodeDbcompliance(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="node",
                  keyword="dbcompliance",
                  order=15,
                  example="https://collector.opensvc.com/init/compliance/call/xmlrpc",
                  default_text="Same protocol, server and port as dbopensvc, but with an different path.",
                  text="Set the uri of the collectors' main xmlrpc server. The path part of the uri can be left unspecified."
                )

class KeywordNodeBranch(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="node",
                  keyword="branch",
                  order=15,
                  example="1.9",
                  text="Set the targeted opensvc agent branch. The downloaded upgrades will honor that branch. If not set, the repopkg imposes the target branch, which is not recommended with a public repopkg."
                )

class KeywordNodeRepo(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="node",
                  keyword="repo",
                  order=15,
                  example="http://opensvc.repo.corp",
                  text="""Set the uri of the opensvc agent package repository and compliance modules gzipped tarball repository. This parameter is used by the 'nodemgr updatepkg' and 'nodemgr updatecomp' commands.

Expected repository structure::

	ROOT
	+- compliance
	 +- compliance-100.tar.gz
	 +- compliance-101.tar.gz
	 +- current -> compliance-101.tar.gz
	+- packages
	 +- deb
	 +- depot
	 +- pkg
	 +- sunos-pkg
	 +- rpms
	  +- current -> 1.9/current
	  +- 1.9
	   +- current -> opensvc-1.9-50.rpm
	   +- opensvc-1.9-49.rpm
	   +- opensvc-1.9-50.rpm
	 +- tbz

"""
                )

class KeywordNodeRepopkg(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="node",
                  keyword="repopkg",
                  order=15,
                  example="http://repo.opensvc.com",
                  text="""Set the uri of the opensvc agent package repository. This parameter is used by the 'nodemgr updatepkg' command.

Expected repository structure::

	ROOT
	+- deb
	+- depot
	+- pkg
	+- sunos-pkg
	+- rpms
	 +- current -> 1.9/current
	 +- 1.9
	  +- current -> opensvc-1.9-50.rpm
	  +- opensvc-1.9-49.rpm
	  +- opensvc-1.9-50.rpm
	+- tbz

"""
                )

class KeywordNodeRepocomp(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="node",
                  keyword="repocomp",
                  order=15,
                  example="http://compliance.repo.corp",
                  text="""Set the uri of the opensvc compliance modules gzipped tarball repository. This parameter is used by the 'nodemgr updatecomp' command.

Expected repository structure::

	ROOT
	+- compliance-100.tar.gz
	+- compliance-101.tar.gz
	+- current -> compliance-101.tar.gz

"""
                )

class KeywordNodeRuser(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="node",
                  keyword="ruser",
                  order=15,
                  example="root opensvc@node1",
                  text="""Set the remote user to use to login to a remote node with ssh and rsync. The remote user must have the privileges to run as root the following commands on the remote node:

* nodemgr
* svcmgr
* rsync

The default ruser is root for all nodes. ruser accepts a list of user[@node] ... If @node is ommited, user is considered the new default user.
"""
                )

class KeywordNodeMaintenanceGracePeriod(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="node",
                  keyword="maintenance_grace_period",
                  convert="duration",
                  order=15,
                  default=60,
                  text="A duration expression, like 1h30m, defining how long the daemon retains a remote in-maintenance node data. As long as the remote node data are retained, the local daemon won't opt-in to takeover its running instances. This parameter should be adjusted to span the node reboot time, so the services have a chance to be restarted on the same node if their placement was optimal."
                )

class KeywordNodeRejoinGracePeriod(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="node",
                  keyword="rejoin_grace_period",
                  convert="duration",
                  order=15,
                  default=90,
                  text="A duration expression, like 90m, defining how long the daemon restrains from taking start decisions if no heartbeat has been received from a peer since daemon startup."
                )

class KeywordNodeReadyPeriod(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="node",
                  keyword="ready_period",
                  convert="duration",
                  order=15,
                  default=5,
                  text="A duration expression, like 10s, defining how long the daemon monitor waits before starting a service instance in ready state. A peer node can preempt the start during this period. The default is 16s. Usually set to allow at least a couple of heartbeats to be received."
                )

class KeywordCenteraSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="centera",
                  keyword="schedule",
                  order=15,
                  text="Schedule parameter for the 'centera' node action. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordXtremioSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="xtremio",
                  keyword="schedule",
                  order=15,
                  text="Schedule parameter for the 'pushxtremio' node action. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordHp3parSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="hp3par",
                  keyword="schedule",
                  order=15,
                  text="Schedule parameter for the 'pushhp3par' node action. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordVnxSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="vnx",
                  keyword="schedule",
                  order=15,
                  text="Schedule parameter for the 'pushvnx' node action. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordFreenasSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="freenas",
                  keyword="schedule",
                  order=15,
                  text="Schedule parameter for the 'pushfreenas' node action. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordGceDisksSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="gcedisks",
                  keyword="schedule",
                  order=15,
                  text="Schedule parameter for the 'pushgcedisks' node action. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordDequeueActionsSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="dequeue_actions",
                  keyword="schedule",
                  order=15,
                  text="Schedule parameter for the 'dequeue actions' node action. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordSysreportSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sysreport",
                  keyword="schedule",
                  order=15,
                  default="00:00-06:00",
                  text="Schedule parameter for the 'sysreport' node action, which check all modules and fix only modules flagged 'autofix'. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordComplianceSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="compliance",
                  keyword="schedule",
                  order=15,
                  default="02:00-06:00",
                  text="Schedule parameter for the 'compliance auto' node action, which check all modules and fix only modules flagged 'autofix'. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordComplianceAutoUpdate(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="compliance",
                  keyword="auto_update",
                  convert="boolean",
                  order=15,
                  default=False,
                  text="If set to True, and if the execution context indicates a scheduled run, execute 'updatecomp' upon 'compliance check'. This toggle helps keep the compliance modules in sync with the reference repository. Beware of the security impact of this setting: you must be careful your module repository is kept secure."
                )

class KeywordStatsSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="stats",
                  keyword="schedule",
                  order=15,
                  default="00:00-06:00",
                  text="Schedule parameter for the 'pushstats' node action. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordStatsDisable(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="stats",
                  keyword="disable",
                  convert="list",
                  order=15,
                  example="blockdev, mem_u",
                  text="Disable push for a stats group (mem_u, cpu, proc, swap, netdev, netdev_err, block, blockdev, fs_u)."
                )

class KeywordChecksSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="checks",
                  keyword="schedule",
                  order=15,
                  default="00:00-06:00",
                  text="Schedule parameter for the 'pushchecks' node action. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordPackagesSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="packages",
                  keyword="schedule",
                  order=15,
                  default="00:00-06:00",
                  text="Schedule parameter for the 'pushpkg' node action. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordPatchesSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="patches",
                  keyword="schedule",
                  order=15,
                  default="00:00-06:00",
                  text="Schedule parameter for the 'pushpatch' node action. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordAssetSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="asset",
                  keyword="schedule",
                  order=15,
                  default="00:00-06:00",
                  text="Schedule parameter for the 'pushasset' node action. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordNsrSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="nsr",
                  keyword="schedule",
                  order=15,
                  text="Schedule parameter for the 'pushnsr' node action. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordDcsSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="dcs",
                  keyword="schedule",
                  order=15,
                  text="Schedule parameter for the 'pushdcs' node action. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordHdsSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="hds",
                  keyword="schedule",
                  order=15,
                  text="Schedule parameter for the 'pushhds' node action. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordNecismSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="necism",
                  keyword="schedule",
                  order=15,
                  text="Schedule parameter for the 'pushnecism' node action. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordEvaSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="eva",
                  keyword="schedule",
                  order=15,
                  text="Schedule parameter for the 'pusheva' node action. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordIbmdsSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="ibmds",
                  keyword="schedule",
                  order=15,
                  text="Schedule parameter for the 'pushibmds' node action. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordIbmsvcSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="ibmsvc",
                  keyword="schedule",
                  order=15,
                  text="Schedule parameter for the 'pushibmsvc' node action. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordVioserverSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="vioserver",
                  keyword="schedule",
                  order=15,
                  text="Schedule parameter for the 'pushvioserver' node action. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordBrocadeSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="brocade",
                  keyword="schedule",
                  order=15,
                  text="Schedule parameter for the 'pushbrocade' node action. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordDisksSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disks",
                  keyword="schedule",
                  order=15,
                  default="00:00-06:00",
                  text="Schedule parameter for the 'pushdisks' node action. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordSymSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sym",
                  keyword="schedule",
                  order=15,
                  text="Schedule parameter for the 'pushsym' node action. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordRotateRootPwSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="rotate_root_pw",
                  keyword="schedule",
                  order=15,
                  text="Schedule parameter for the 'rotate root pw' node action. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordStatsCollectionSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="stats_collection",
                  keyword="schedule",
                  order=15,
                  default="@10",
                  text="Schedule parameter for the 'collect stats' node action. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordRebootSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="reboot",
                  keyword="schedule",
                  order=15,
                  text="Schedule parameter for the 'auto reboot' node action. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordRebootOnce(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="reboot",
                  keyword="once",
                  convert="boolean",
                  order=15,
                  default=True,
                  text="""If once is set to false, do not remove the reboot flag before rebooting,
so that the node is ready to reboot again in the next allowed timerange.
This setup is needed to enforce a periodic reboot, with a patching script
hooked as a pre trigger for example.

If not set, or set to true, the reboot flag is removed before reboot, and a 'nodemgr schedule reboot' is needed to rearm.
"""
                )

class KeywordRebootPre(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="reboot",
                  keyword="pre",
                  convert="shlex",
                  order=15,
                  example="yum upgrade -y",
                  text="A command to execute before reboot. Errors are ignored."
                )

class KeywordRebootBlockingPre(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="reboot",
                  keyword="blocking_pre",
                  convert="shlex",
                  order=15,
                  example="yum upgrade -y",
                  text="A command to execute before reboot. Abort the reboot on error."
                )

class KeywordListenerAddr(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="listener",
                  keyword="addr",
                  order=15,
                  default="0.0.0.0",
                  example="1.2.3.4",
                  text="The ip addr the daemon listener must listen on."
                )

class KeywordListenerPort(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="listener",
                  keyword="port",
                  order=15,
                  default=1214,
                  text="""The port the daemon listener must listen on. In pull action mode, the collector sends a tcp packet to the server to notify there are actions to unqueue. The opensvc daemon executes the 'dequeue actions' node action upon receive. The listener.port parameter is sent to the collector upon pushasset. The collector uses this port to notify the node."""
                )

class KeywordSyslogFacility(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="syslog",
                  keyword="facility",
                  order=15,
                  default="daemon",
                  text="""The syslog facility to log to."""
                )

class KeywordSyslogLevel(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="syslog",
                  keyword="level",
                  order=15,
                  default="daemon",
                  candidates=["critical", "error", "warning", "info", "debug"],
                  text="The minimum message criticity to feed to syslog. Setting to critical actually disables the syslog logging, as the agent does not emit message at this level."
                )

class KeywordSyslogHost(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="syslog",
                  keyword="host",
                  order=15,
                  default_text="localhost if port is set",
                  text="The syslog host to send logs to. If neither host nor port are specified and if /dev/log exists, the messages are posted to /dev/log."
                )

class KeywordSyslogPort(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="syslog",
                  keyword="port",
                  order=15,
                  default=514,
                  text="The syslog host to send logs to. If neither host nor port are specified and if /dev/log exists, the messages are posted to /dev/log."
                )

class KeywordClusterDns(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="cluster",
                  keyword="dns",
                  convert="list",
                  default=[],
                  at=True,
                  order=15,
                  text="The list of nodes to set as dns in the containers resolvers. If set, the search will also be set to <svcname>.<clustername> and <clustername>."
                )

class KeywordClusterId(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="cluster",
                  keyword="id",
                  at=True,
                  order=15,
                  default_text="<auto-generated>",
                  text="This information is fetched from the join command payload received from the joined node."
                )

class KeywordClusterName(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="cluster",
                  keyword="name",
                  at=True,
                  order=15,
                  default="default",
                  text="This information is fetched from the join command payload received from the joined node."
                )

class KeywordClusterSecret(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="cluster",
                  keyword="secret",
                  at=True,
                  order=15,
                  default_text="<random autogenerated on first use>",
                  text="The cluster shared secret. Used to encrypt/decrypt data with AES256. This secret is either autogenerated or fetched from a join command."
                )

class KeywordClusterNodes(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="cluster",
                  keyword="nodes",
                  convert="list",
                  order=15,
                  text="This list is fetched from the join command payload received from the joined node. The service configuration {clusternodes} is resolved to this keyword value."
                )

class KeywordClusterDrpNodes(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="cluster",
                  keyword="drpnodes",
                  convert="list",
                  order=15,
                  text="This list is fetched from the join command payload received from the joined node. The service configuration {clusterdrpnodes} is resolved to this keyword value."
                )

class KeywordClusterQuorum(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="cluster",
                  keyword="quorum",
                  convert="boolean",
                  order=15,
                  text="Should a split segment of the cluster commit suicide. Default is False. If set to true, please set at least 2 arbitrators so you can rolling upgrade the opensvc daemons."
                )

class KeywordArbitratorName(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="arbitrator",
                  keyword="name",
                  order=15,
                  required=True,
                  text="""The arbitrator resolvable node name.

An arbitrator is a opensvc node (running the usual osvc daemon) this
cluster nodes can ask for a vote when the cluster is split.

Arbitrators are tried in sequence, the first reachable arbitrator
gives a vote. In case of a real split, all arbitrators are expected to
be unreachable from the lost segment. At least one of them is
expected to be reachable from the surviving segment.

Arbitrators of a cluster must thus be located close enough to each
other, so a subset of arbitrators can't be reachable from a split
cluster segment, while another subset of arbitrators is reachable
from the other split cluster segment. But not close enough so they can
all fail together. Usually, this can be interpreted as: same site,
not same rack and power lines.

Arbitrators usually don't run services, even though they could, as their
secret might be known by multiple clusters of different responsibles.

Arbitrators can be tested using "nodemgr ping --node <arbitrator name>".
"""
                )

class KeywordArbitratorSecret(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="arbitrator",
                  keyword="secret",
                  order=15,
                  required=True,
                  text="The secret to use to encrypt/decrypt data exchanged with the arbitrator (AES256)."
                )

class KeywordStonithCmd(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="stonith",
                  keyword="cmd",
                  at=True,
                  convert="shlex",
                  order=15,
                  required=True,
                  example="/bin/true",
                  text="The command to use to STONITH a peer. Usually comes from a fencing utilities collection."
                )

class KeywordHbType(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="hb",
                  keyword="type",
                  candidates=["unicast", "multicast", "disk", "relay"],
                  order=15,
                  required=True,
                  text="The heartbeat driver name."
                )

class KeywordHbUcastAddr(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="hb",
                  keyword="addr",
                  rtype="unicast",
                  at=True,
                  order=15,
                  example="1.2.3.4",
                  default_text="0.0.0.0 for listening and to the resolved nodename for sending.",
                  text="The ip address of each node."
                )

class KeywordHbUcastIntf(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="hb",
                  keyword="intf",
                  rtype="unicast",
                  at=True,
                  order=15,
                  default_text="The natural interface for <addr>",
                  example="eth0",
                  text="The interface to bind."
                )

class KeywordHbUcastPort(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="hb",
                  keyword="port",
                  rtype="unicast",
                  at=True,
                  order=15,
                  default=10000,
                  text="The port for each node to send to or listen on."
                )

class KeywordHbTimeout(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="hb",
                  keyword="timeout",
                  at=True,
                  order=15,
                  default=15,
                  text="The delay since the last received heartbeat from a node before considering this node is gone."
                )

class KeywordHbMcastAddr(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="hb",
                  keyword="addr",
                  rtype="multicast",
                  at=True,
                  order=15,
                  default="224.3.29.71",
                  text="The multicast address to send to and listen on."
                )

class KeywordHbMcastIntf(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="hb",
                  keyword="intf",
                  rtype="multicast",
                  at=True,
                  order=15,
                  default_text="The natural interface for <addr>",
                  example="eth0",
                  text="The interface to bind."
                )

class KeywordHbMcastPort(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="hb",
                  keyword="port",
                  rtype="multicast",
                  at=True,
                  order=15,
                  default=10000,
                  text="The port for each node to send to or listen on."
                )

class KeywordHbDiskDev(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="hb",
                  keyword="dev",
                  rtype="disk",
                  required=True,
                  order=15,
                  text="The device to write the hearbeats to and read from. It must be dedicated to the daemon use. Its size should be 1M + 1M per cluster node."
                )

class KeywordHbRelayRelay(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="hb",
                  keyword="relay",
                  rtype="relay",
                  order=15,
                  required=True,
                  example="relaynode1",
                  text="The relay resolvable node name."
                )

class KeywordHbRelaySecret(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="hb",
                  keyword="secret",
                  rtype="relay",
                  order=15,
                  required=True,
                  example="123123123124325543565",
                  text="The secret to use to encrypt/decrypt data exchanged with the relay (AES256)."
                )

class KeywordCniPlugins(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="cni",
                  keyword="plugins",
                  order=15,
                  default="/opt/cni/bin",
                  text="The directory hosting the CNI plugins."
                )

class KeywordCniConfig(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="cni",
                  keyword="config",
                  order=15,
                  default="/opt/cni/net.d",
                  text="The directory hosting the CNI network configuration files."
                )


class KeyDict(KeywordStore):
    def __init__(self):
        KeywordStore.__init__(
           self,
           deprecated_keywords=DEPRECATED_KEYWORDS,
           deprecated_sections=DEPRECATED_SECTIONS,
           template_prefix="template.node.",
           base_sections=BASE_SECTIONS,
           has_default_section=False,
        )

        self += KeywordNodeMinAvailMem()
        self += KeywordNodeMinAvailSwap()
        self += KeywordNodeUuid()
        self += KeywordNodeEnv()
        self += KeywordNodeAssetEnv()
        self += KeywordNodeMaxParallel()
        self += KeywordNodePrkey()
        self += KeywordNodeLocCountry()
        self += KeywordNodeLocCity()
        self += KeywordNodeLocZip()
        self += KeywordNodeLocAddr()
        self += KeywordNodeLocBuilding()
        self += KeywordNodeLocFloor()
        self += KeywordNodeLocRoom()
        self += KeywordNodeLocRack()
        self += KeywordNodeSecZone()
        self += KeywordNodeTeamInteg()
        self += KeywordNodeTeamSupport()
        self += KeywordNodeConnectTo()
        self += KeywordNodeDbopensvc()
        self += KeywordNodeDbcompliance()
        self += KeywordNodeBranch()
        self += KeywordNodeRepo()
        self += KeywordNodeRepopkg()
        self += KeywordNodeRepocomp()
        self += KeywordNodeRuser()
        self += KeywordNodeMaintenanceGracePeriod()
        self += KeywordNodeRejoinGracePeriod()
        self += KeywordNodeReadyPeriod()
        self += KeywordXtremioSchedule()
        self += KeywordHp3parSchedule()
        self += KeywordVnxSchedule()
        self += KeywordCenteraSchedule()
        self += KeywordFreenasSchedule()
        self += KeywordGceDisksSchedule()
        self += KeywordDequeueActionsSchedule()
        self += KeywordSysreportSchedule()
        self += KeywordComplianceSchedule()
        self += KeywordComplianceAutoUpdate()
        self += KeywordStatsSchedule()
        self += KeywordStatsCollectionSchedule()
        self += KeywordStatsDisable()
        self += KeywordChecksSchedule()
        self += KeywordPackagesSchedule()
        self += KeywordPatchesSchedule()
        self += KeywordAssetSchedule()
        self += KeywordNsrSchedule()
        self += KeywordDcsSchedule()
        self += KeywordHdsSchedule()
        self += KeywordNecismSchedule()
        self += KeywordEvaSchedule()
        self += KeywordIbmsvcSchedule()
        self += KeywordIbmdsSchedule()
        self += KeywordVioserverSchedule()
        self += KeywordBrocadeSchedule()
        self += KeywordDisksSchedule()
        self += KeywordSymSchedule()
        self += KeywordRotateRootPwSchedule()
        self += KeywordRebootSchedule()
        self += KeywordRebootOnce()
        self += KeywordRebootPre()
        self += KeywordRebootBlockingPre()
        self += KeywordListenerAddr()
        self += KeywordListenerPort()
        self += KeywordSyslogFacility()
        self += KeywordSyslogLevel()
        self += KeywordSyslogHost()
        self += KeywordSyslogPort()
        self += KeywordClusterDns()
        self += KeywordClusterId()
        self += KeywordClusterName()
        self += KeywordClusterSecret()
        self += KeywordClusterNodes()
        self += KeywordClusterDrpNodes()
        self += KeywordClusterQuorum()
        self += KeywordArbitratorName()
        self += KeywordArbitratorSecret()
        self += KeywordStonithCmd()
        self += KeywordHbType()
        self += KeywordHbTimeout()
        self += KeywordHbUcastAddr()
        self += KeywordHbUcastIntf()
        self += KeywordHbUcastPort()
        self += KeywordHbMcastAddr()
        self += KeywordHbMcastIntf()
        self += KeywordHbMcastPort()
        self += KeywordHbDiskDev()
        self += KeywordHbRelayRelay()
        self += KeywordHbRelaySecret()
        self += KeywordCniPlugins()
        self += KeywordCniConfig()

KEYS = KeyDict()

if __name__ == "__main__":
    if len(sys.argv) == 2:
        fmt = sys.argv[1]
    else:
        fmt = "text"

    KEYS.print_templates(fmt=fmt)
  0707010005c8d5000081b40000000000000000000000015ab2175400000a05000000660000000000000000000000000000002600000000root/usr/share/opensvc/lib/nodemgr.py from __future__ import print_function
from __future__ import absolute_import

import sys
import os
import optparse

#
# add project lib to path
#
prog = "nodemgr"

import rcStatus
import rcColor
import rcExceptions as ex
from rcGlobalEnv import *
from rcUtilities import ximport
from nodemgr_parser import NodemgrOptParser

node_mod = ximport('node')

def get_extra_argv(argv=None):
    """
    Extract extra argv from nodemgr argv.

    nodemgr can act as a wrapper for other commands (storage drivers for
    example).
    """
    if argv is None:
        argv = sys.argv[1:]
    if len(argv) < 2:
        return argv, []

    if "array" in argv:
        pos = argv.index('array')
    elif "cli" in argv:
        pos = argv.index('cli')
        if pos > 0 and argv[pos-1] != "collector":
            return argv, []
    else:
        return argv, []

    if "--" in argv:
        pos = argv.index("--")
    if len(argv) > pos + 1:
        extra_argv = argv[pos+1:]
    else:
        extra_argv = []
    argv = argv[:pos+1]
    return argv, extra_argv

def do_symcli_db_file(options):
    try:
        symcli_db_file = options.symcli_db_file
    except AttributeError:
        return
    if symcli_db_file is None:
        return
    if not os.path.exists(symcli_db_file):
        print("File does not exist: %s" % symcli_db_file)
        return
    os.environ['SYMCLI_DB_FILE'] = symcli_db_file
    os.environ['SYMCLI_OFFLINE'] = '1'

def _main(node, argv=None):
    argv, extra_argv = get_extra_argv(argv)
    optparser = NodemgrOptParser(argv)
    options, action = optparser.parse_args(argv)
    options.extra_argv = extra_argv

    rcColor.use_color = options.color
    node.options.update(options.__dict__)
    do_symcli_db_file(options)

    if action.startswith("collector_cli"):
        action = "collector_cli"

    node.check_privs(action)

    err = 0
    try:
        err = node.action(action)
    except KeyboardInterrupt:
        sys.stderr.write("Keybord Interrupt\n")
        err = 1
    except ex.excError:
        import traceback
        exc_type, exc_value, exc_traceback = sys.exc_info()
        es = str(exc_value)
        if len(es) > 0:
            sys.stderr.write(str(exc_value)+'\n')
        err = 1
    except:
        raise
        err = 1
    return err

def main(argv=None):
    node = node_mod.Node()

    try:
        return _main(node, argv=argv)
    except ex.excError as exc:
        print(exc, file=sys.stderr)
        return 1
    finally:
        node.close()

if __name__ == "__main__":
    ret = main()
    sys.exit(ret)

   0707010005c8d6000081b40000000000000000000000015ab2175400008fdd000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/nodemgr_parser.py  """
nodemgr command line actions and options
"""
from rcGlobalEnv import Storage
from rcOptParser import OptParser
from optparse import Option

PROG = "nodemgr"

OPT = Storage({
    "api": Option(
        "--api", default=None, action="store", dest="api",
        help="Specify a collector api url different from the "
             "one set in node.conf."),
    "add": Option(
        "--add", default=None,
        action="store",
        help="A list member to add to the value pointed by :opt:``--param``. "
             "If :opt:``--index`` is set, insert the new element at the "
             "specified position in the list."),
    "app": Option(
        "--app", default=None, action="store", dest="app",
        help="Optional with the register command. Register the "
             "node in the specified app. If not specified, the "
             "node is registered in the first registering "
             "user's app found."),
    "attach": Option(
        "--attach", default=False,
        action="store_true", dest="attach",
        help="Attach the modulesets specified in a compliance run."),
    "author": Option(
        "--author", default=None,
        action="store", dest="author",
        help="The acker name to log when acknowledging action log errors"),
    "backlog": Option(
        "--backlog", default=None,
        action="store", dest="backlog",
        help="A size expression limiting the volume of data fetched "
             "from the log file tail. Default is 10k."),
    "begin": Option(
        "--begin", default=None,
        action="store", dest="begin",
        help="A begin date expressed as ``YYYY-MM-DD hh:mm`` limiting the "
             "timerange the action applies to."),
    "broadcast": Option(
        "--broadcast", default=None,
        action="store", dest="broadcast",
        help="A list of broadcast addresses, comma separated, the send "
             "the Wake-On-LAN packets to."),
    "color": Option(
        "--color", default="auto",
        action="store", dest="color",
        help="Colorize output. Possible values are:\n\n"
             "* auto: guess based on tty presence\n"
             "* always|yes: always colorize\n"
             "* never|no: never colorize"),
    "comment": Option(
        "--comment", default=None,
        action="store", dest="comment",
        help="A comment to log when acknowldging action log error entries."),
    "config": Option(
        "--config", default=None, action="store", dest="config",
        help="Specify a user-specific collector api connection "
             "configuration file. Defaults to '~/.opensvc-cli'."),
    "cron": Option(
        "--cron", default=False,
        action="store_true", dest="cron",
        help="If set, the action is actually executed only if the scheduling"
             "constraints are satisfied."),
    "debug": Option(
        "--debug", default=False,
        action="store_true", dest="debug",
        help="Increase stream log verbosity up to the debug level."),
    "devices": Option(
        "--dev", default=[], action="append", dest="devices",
        help="A device path to limit or apply the action to."),
    "discard": Option(
        "--discard", default=False,
        action="store_true", dest="discard",
        help="Discard the stashed, invalid, configuration file."),
    "duration": Option(
        "--duration", default=None,
        action="store", dest="duration",
        help="A duration expression like, ``1h10m``."),
    "end": Option(
        "--end", default=None,
        action="store", dest="end",
        help="A end date expressed as ``YYYY-MM-DD hh:mm`` limiting the "
             "timerange the action applies to."),
    "eval": Option(
        "--eval", default=False,
        action="store_true", dest="eval",
        help="If set with the :cmd:`svcmgr get` action, the printed value of "
             ":opt:`--param` is evaluated, scoped and dereferenced."),
    "filterset": Option(
        "--filterset", default="",
        action="store", dest="filterset",
        help="Specify a filterset to limit collector extractions."),
    "follow": Option(
        "--follow", default=False,
        action="store_true", dest="follow",
        help="Follow the logs as they come. Use crtl-c to interrupt."),
    "force": Option(
        "--force", default=False,
        action="store_true", dest="force",
        help="Force action, ignore sanity checks."),
    "format": Option(
        "--format", default=None, action="store", dest="format",
        help="Specify a data formatter. Possible values are json, csv"
             " or table."),
    "hba": Option(
        "--hba", default=None, action="store", dest="hba",
        help="Specify a hba to scan for new block devices. Example: "
             "5001438002432430 or iqn.1993-08.org.debian:01:659b4bbd68bd."),
    "help": Option(
        "-h", "--help", default=None,
        action="store_true", dest="parm_help",
        help="Show this help message and exit"),
    "id": Option(
        "--id", default=0,
        action="store", dest="id",
        help="Specify an id to act on."),
    "index": Option(
        "--index", default=None,
        action="store", type="int",
        help="The position in the list pointed by --param where to add "
             "the new list element on a set action"),
    "insecure": Option(
        "--insecure", default=None,
        action="store_true", dest="insecure",
        help="Allow communications with a collector presenting an "
             "unverified SSL certificate."),
    "kw": Option(
        "--kw", action="append", dest="kw",
        help="An expression like ``[<section>.]<keyword>[@<scope>][[<index>]]<op><value>`` where\n\n"
             "* <section> can be:\n\n"
             "  * a resource id\n"
             "  * a resource driver group name (fs, ip, ...). In this case, the set applies to all matching resources.\n"
             "* <op> can be:\n\n"
             "  * ``=``\n"
             "  * ``+=``\n"
             "  * ``-=``\n\n"
             "Multiple --kw can be set to apply multiple configuration change "
             "in a file with a single write.\n\n"
             "Examples:\n\n"
             "* app.start=false\n"
             "  Turn off app start for all app resources\n"
             "* app#1.start=true\n"
             "  Turn on app start for app#1\n"
             "* nodes+=node3\n"
             "  Append node3 to nodes\n"
             "* nodes[0]+=node3\n"
             "  Preprend node3 to nodes\n"),
    "like": Option(
        "--like", default="%",
        action="store", dest="like",
        help="A data filtering expression. ``%`` is the multi-character "
             "wildcard. ``_`` is the single-character wildcard. Leading and "
             "trailing ``%`` are automatically set."),
    "local": Option(
        "--local", default=False,
        action="store_true", dest="local",
        help="Set to disable cluster-wide operations."),
    "lun": Option(
        "--lun", default=None, action="store", dest="lun",
        help="Specify a logical unit number to scan for new block devices. "
             "Example: 1."),
    "mac": Option(
        "--mac", default=None,
        action="store", dest="mac",
        help="A list of mac addresses, comma separated, used as target of "
             "the Wake-On-LAN packets."),
    "message": Option(
        "--message", default="",
        action="store", dest="message",
        help="The message to send to the collector for logging"),
    "module": Option(
        "--module", default="",
        action="store", dest="module",
        help="Specify the modules to limit the run to. The modules must be in already attached modulesets."),
    "moduleset": Option(
        "--moduleset", default="",
        action="store", dest="moduleset",
        help="Specify the modulesets to limit the action to. The special value ``all`` "
             "can be used in conjonction with detach."),
    "node": Option(
        "--node", default="",
        action="store", dest="node",
        help="The node to send a request to. If not specified the local node is targeted."),
    "nopager": Option(
        "--no-pager", default=False,
        action="store_true", dest="nopager",
        help="Do not display the command result in a pager."),
    "opt_object": Option(
        "--object", default=[], action="append", dest="objects",
        help="An object to limit a push* action to. Multiple "
             "--object <object id> parameters can be set on a "
             "single command line."),
    "param": Option(
        "--param", default=None,
        action="store", dest="param",
        help="An expression like ``[<section>.]<keyword>`` where\n\n"
             "* <section> can be:\n\n"
             "  * a resource id\n"
             "  * a resource driver group name (fs, ip, ...). In this case, the set applies to all matching resources."),
    "password": Option(
        "--password", default=None,
        action="store", dest="password",
        help="Authenticate with the collector using the "
             "specified user credentials instead of the node "
             "credentials. Prompted if necessary but not "
             "specified."),
    "port": Option(
        "--port", default=7,
        action="store", dest="port",
        help="A list of ports, comma separated, used as target of "
             "the Wake-On-LAN packets."),
    "recover": Option(
        "--recover", default=False,
        action="store_true", dest="recover",
        help="Recover the stashed erroneous configuration file "
             "in a :cmd:`nodemgr edit config` command"),
    "refresh_api": Option(
        "--refresh-api", default=False,
        action="store_true", dest="refresh_api",
        help="Force a reload of the collector's api digest."),
    "remove": Option(
        "--remove", default=None,
        action="store",
        help="A list member to drop from the value pointed by :kw:`--param`."),
    "reverse": Option(
        "--reverse", default=False,
        action="store_true", dest="reverse",
        help="Print the tree leaf-to-root."),
    "ruleset": Option(
        "--ruleset", default="",
        action="store", dest="ruleset",
        help="Specify the rulesets to limit the action to. The special value ``all`` "
             "can be used in conjonction with detach."),
    "ruleset_date": Option(
        "--ruleset-date", default="",
        action="store", dest="ruleset_date",
        help="Use an historical ruleset, specified by its date."),
    "save": Option(
        "--save", default=False,
        action="store_true", dest="save",
        help="Save the collector cli settings to the file specified by --config or ~/.opensvc-cli by default."),
    "stats_dir": Option(
        "--stats-dir", default=None,
        action="store", dest="stats_dir",
        help="Points the directory where the metrics files are "
             "stored for pushstats."),
    "secret": Option(
        "--secret", default=None,
        action="store", dest="secret",
        help="The cluster secret used as the AES key in the cluster "
             "communications."),
    "symcli_db_file": Option(
        "--symcli-db-file", default=None,
        action="store", dest="symcli_db_file",
        help="Use symcli offline mode with the "
             "specified file. The aclx files are expected to be "
             "found in the same directory and named either "
             "<symid>.aclx or <same_prefix_as_bin_file>.aclx."),
    "sync": Option(
        "--sync", default=False,
        action="store_true", dest="syncrpc",
        help="Use synchronous collector communication. For example, "
             ":cmd:`pushasset --sync` before a compliance run makes sure "
             "the pushed data has hit the collector database before the "
             "rulesets are contextualized."),
    "tag": Option(
        "--tag", default=None,
        action="store", dest="tag",
        help="The tag name, as shown by :cmd:`nodemgr collector list tags`."),
    "target": Option(
        "--target", default=None, action="store", dest="target",
        help="Specify a target to scan for new block devices. Example: "
             "5000097358185088 or iqn.clementine.tgt1."),
    "thr_id": Option(
        "--thread-id", default=None, action="store", dest="thr_id",
        help="Specify a daemon thread, as listed in the :cmd:`nodemgr daemon "
             "status` output."),
    "time": Option(
        "--time", default=300,
        action="store", dest="time", type="int",
        help="Number of seconds to wait for an async action to "
             "finish. Default is 300 seconds."),
    "user": Option(
        "--user", default=None, action="store", dest="user",
        help="Authenticate with the collector using the "
             "specified user credentials instead of the node "
             "credentials. Required with :cmd:`nodenmgr register` "
             "when the collector is configured to refuse "
             "anonymous register."),
    "value": Option(
        "--value", default=None,
        action="store", dest="value",
        help="The value to set for the keyword pointed by :opt:`--param`"),
    "verbose": Option(
        "--verbose", default=False,
        action="store_true", dest="verbose",
        help="Include more information to some print commands output. "
             "For example, add the ``next run`` column in the output of "
             ":cmd:`nodemgr print schedule`."),
    "wait": Option(
        "--wait", default=False,
        action="store_true", dest="wait",
        help="Wait for asynchronous action termination"),
})

GLOBAL_OPTS = [
    OPT.color,
    OPT.debug,
    OPT.format,
    OPT.help,
]

ASYNC_OPTS = [
    OPT.time,
    OPT.wait,
]

DAEMON_OPTS = [
    OPT.local,
    OPT.node,
]

ACTIONS = {
    "Node actions": {
        "auto_reboot": {
            "msg": "Reboot the node if in the specified schedule.",
            "options": [
                OPT.cron,
            ],
        },
        "events": {
            "msg": "Follow the daemon events",
            "options": [
                OPT.local,
                OPT.node,
            ]
        },
        "frozen": {
            "msg": "Return 0 if the services are frozen node-wide, "
                   "preventing the daemon to orchestrate them. Return 1 "
                   "otherwise",
        },
        "freeze": {
            "msg": "Freeze services node-wide, preventing the daemon to "
                   "orchestrate them. This freeze method preserves the "
                   "frozen state at service-level (with svcmgr).",
            "options": ASYNC_OPTS + DAEMON_OPTS,
        },
        "thaw": {
            "msg": "Thaw services node-wide, allowing the daemon to "
                   "orchestrate them. This thaw method does not actually "
                   "thaw services frozen at service-level (with svcmgr).",
            "options": ASYNC_OPTS + DAEMON_OPTS,
        },
        "logs": {
            "msg": "Display of the nodemgr and daemon logs.",
            "options": [
                OPT.backlog,
                OPT.follow,
                OPT.local,
                OPT.node,
            ]
        },
        "ping": {
            "msg": "Ping a cluster node or arbitrator node. The ping "
                   "validates the remote is functional.",
            "options": [
                OPT.node,
            ]
        },
        "shutdown": {
            "msg": "Shutdown the node to powered off state.",
            "options": DAEMON_OPTS,
        },
        "reboot": {
            "msg": "Reboot the node.",
            "options": DAEMON_OPTS,
        },
        "schedule_reboot_status": {
            "msg": "Tell if the node is scheduled for reboot.",
        },
        "schedule_reboot": {
            "msg": "Mark the node for reboot at the next allowed period. The "
                   "allowed period is defined by a 'reboot' section in "
                   "node.conf.",
        },
        "unschedule_reboot": {
            "msg": "Unmark the node for reboot at the next allowed period.",
        },
        "array": {
            "msg": "Pass a command to a supported array whose access method "
                   "and credentials are defined in auth.conf.",
        },
        "updatepkg": {
            "msg": "Upgrade the opensvc agent version. the packages must be "
                   "available behind the node.repo/packages url.",
            "options": DAEMON_OPTS,
        },
        "updatecomp": {
            "msg": "Upgrade the opensvc compliance modules. The modules must "
                   "be available as a tarball behind the :kw:`node.repocomp` "
                   "url.",
            "options": DAEMON_OPTS,
        },
        "scanscsi": {
            "msg": "Scan the scsi hosts in search of new disks.",
            "options": [
                OPT.hba,
                OPT.target,
                OPT.lun,
            ],
        },
        "dequeue_actions": {
            "msg": "Dequeue and execute actions from the collector's action "
                   "queue for this node and its services.",
            "options": [
                OPT.cron,
            ],
        },
        "rotate_root_pw": {
            "msg": "Set a new root password and store it in the collector.",
            "options": [
                OPT.cron,
            ],
        },
        "print_devs": {
            "msg": "Print the node devices tree.",
            "options": [
                OPT.devices,
                OPT.reverse,
                OPT.verbose,
            ],
        },
        "print_schedule": {
            "msg": "Print the node tasks schedule.",
            "options": [
                OPT.verbose,
            ],
        },
        "stonith": {
            "msg": "Command executed by the daemon monitor to fence peer "
                   "node upon failover when the node previously running "
                   "the service is stale.",
            "options": [
                OPT.node,
            ],
        },
        "wol": {
            "msg": "Forge and send a udp Wake-On-LAN packet to the mac addresses "
                   "specified by :opt:`--mac` and :opt:`--broadcast` arguments.",
            "options": [
                OPT.broadcast,
                OPT.mac,
                OPT.port,
            ],
        },
        "collect_stats": {
            "msg": "Write in local files metrics not found in the standard "
                   "metrics collector. These files will be fed to the "
                   "collector by the :cmd:`pushstat` action.",
            "options": [
                OPT.cron,
            ],
        },
    },
    "Network actions": {
        "network_ls": {
            "msg": "List the available networks.",
        },
        "network_show": {
            "msg": "Show a network configuration.",
            "options": [
                OPT.id,
            ],
        },
    },
    "Service actions": {
        "discover": {
            "msg": "Discover vservices accessible from this host. Typically executed on cloud nodes.",
        },
    },
    "Node configuration": {
        "print_config": {
            "msg": "Display the node current configuration.",
        },
        "print_authconfig": {
            "msg": "Display the node current authentication configuration.",
        },
        "edit_config": {
            "msg": "Edit the node configuration.",
            "options": [
                OPT.discard,
                OPT.recover,
            ],
        },
        "edit_authconfig": {
            "msg": "Edit the node authentication configuration.",
        },
        "register": {
            "msg": "Obtain a registration id from the collector. This is is "
                   "then used to authenticate the node in collector communications.",
            "options": [
                OPT.app,
                OPT.password,
                OPT.user,
            ],
        },
        "get": {
            "msg": "Get the raw or evaluated value of a node "
                   "configuration keyword.",
            "options": [
                OPT.eval,
                OPT.param,
                OPT.kw,
            ],
        },
        "set": {
            "msg": "Set a service configuration parameter.",
            "options": [
                OPT.kw,
                OPT.param,
                OPT.value,
                OPT.add,
                OPT.remove,
                OPT.index,
            ],
        },
        "unset": {
            "msg": "Unset a node configuration parameter.",
            "options": [
                OPT.param,
                OPT.kw,
            ],
        },
        "validate_config": {
            "msg": "Check the section names and keywords are valid.",
        },
    },
    "Node daemon management": {
        "daemon_blacklist_status": {
            "msg": "Show the content of the daemon senders blacklist.",
         },
        "daemon_blacklist_clear": {
            "msg": "Empty the content of the daemon senders blacklist.",
         },
        "daemon_restart": {
            "msg": "Restart the daemon.",
         },
        "daemon_running": {
            "msg": "Return with code 0 if the daemon is running, else return "
                   "with code 1",
         },
        "daemon_shutdown": {
            "msg": "Stop all local services instances then stop the daemon.",
         },
        "daemon_status": {
            "msg": "Display the daemon status.",
            "options": DAEMON_OPTS,
         },
        "daemon_start": {
            "msg": "Start the daemon or a daemon thread pointed by :opt:`--thread-id`.",
            "options": DAEMON_OPTS + [
                OPT.thr_id,
            ],
         },
        "daemon_stop": {
            "msg": "Stop the daemon or a daemon thread pointed by :opt:`--thread-id`.",
            "options": DAEMON_OPTS + [
                OPT.thr_id,
            ],
         },
        "daemon_join": {
            "msg": "Join the cluster of the node specified by :opt:`--node <node>`, authenticating with :opt:`--secret <secret>`.",
            "options": DAEMON_OPTS + [
                OPT.secret,
            ],
         },
        "daemon_leave": {
            "msg": "Inform peer nodes we leave the cluster. Make sure the "
                   "left nodes are no longer in the services nodes list "
                   "before leaving, so the other nodes won't takeover.",
         },
    },
    "Push data to the collector": {
        "pushasset": {
            "msg": "Push asset information to collector.",
            "options": [
                OPT.sync,
                OPT.cron,
            ],
         },
        "pushstats": {
            "msg": "Push performance metrics to collector. By default pushed "
                   "stats interval begins yesterday at the beginning of the "
                   "allowed interval and ends now. This interval can be "
                   "changed using --begin/--end parameters. The location "
                   "where stats files are looked up can be changed using "
                   "--stats-dir.",
            "options": [
                OPT.begin,
                OPT.end,
                OPT.stats_dir,
                OPT.cron,
            ],
         },
        "pushdisks": {
            "msg": "Push disks usage information to the collector.",
            "options": [
                OPT.cron,
            ],
         },
        "pushpkg": {
            "msg": "Push package/version list to the collector.",
            "options": [
                OPT.cron,
            ],
         },
        "pushpatch": {
            "msg": "Push patch/version list to the collector.",
            "options": [
                OPT.cron,
            ],
         },
        "pushsym": {
            "msg": "Push symmetrix configurations to the collector.",
            "options": [
                OPT.cron,
                OPT.opt_object,
                OPT.symcli_db_file,
            ],
         },
        "pushemcvnx": {
            "msg": "Push EMC CX/VNX configurations to the collector.",
            "options": [
                OPT.cron,
                OPT.opt_object,
            ],
         },
        "pushcentera": {
            "msg": "Push EMC Centera configurations to the collector.",
            "options": [
                OPT.cron,
                OPT.opt_object,
            ],
         },
        "pushnetapp": {
            "msg": "Push Netapp configurations to the collector.",
            "options": [
                OPT.cron,
                OPT.opt_object,
            ],
         },
        "pusheva": {
            "msg": "Push HP EVA configurations to the collector.",
            "options": [
                OPT.cron,
                OPT.opt_object,
            ],
         },
        "pushnecism": {
            "msg": "Push NEC ISM configurations to the collector.",
            "options": [
                OPT.cron,
                OPT.opt_object,
            ],
         },
        "pushhds": {
            "msg": "Push HDS configurations to the collector.",
            "options": [
                OPT.cron,
                OPT.opt_object,
            ],
         },
        "pushdcs": {
            "msg": "Push Datacore configurations to the collector.",
            "options": [
                OPT.cron,
                OPT.opt_object,
            ],
         },
        "pushfreenas": {
            "msg": "Push FreeNAS configurations to the collector.",
            "options": [
                OPT.cron,
                OPT.opt_object,
            ],
         },
        "pushxtremio": {
            "msg": "Push XtremIO configurations to the collector.",
            "options": [
                OPT.cron,
                OPT.opt_object,
            ],
         },
        "pushibmsvc": {
            "msg": "Push IBM SVC configurations to the collector.",
            "options": [
                OPT.cron,
                OPT.opt_object,
            ],
         },
        "pushhp3par": {
            "msg": "Push HP 3par configurations to the collector.",
            "options": [
                OPT.cron,
                OPT.opt_object,
            ],
         },
        "pushibmds": {
            "msg": "Push IBM DS configurations to the collector.",
            "options": [
                OPT.cron,
                OPT.opt_object,
            ],
         },
        "pushvioserver": {
            "msg": "Push IBM VIO server configurations to the collector.",
            "options": [
                OPT.cron,
                OPT.opt_object,
            ],
         },
        "pushgcedisks": {
            "msg": "Push Google Compute Engine disks configurations to the "
                   "collector.",
            "options": [
                OPT.cron,
                OPT.opt_object,
            ],
         },
        "pushbrocade": {
            "msg": "Push Brocade switch configuration to the collector.",
            "options": [
                OPT.cron,
                OPT.opt_object,
            ],
         },
        "pushnsr": {
            "msg": "Push EMC Networker index to the collector.",
            "options": [
                OPT.cron,
                OPT.opt_object,
            ],
         },
        "sysreport": {
            "msg": "Push system report to the collector for archiving and "
                   "diff analysis.",
            "options": [
                OPT.cron,
            ],
         },
        "checks": {
            "msg": "Run node health checks. Push results to collector.",
            "options": [
                OPT.cron,
            ],
         },
    },
    "Misc": {
        "prkey": {
            "msg": "Show the scsi3 persistent reservation key of this node.",
         },
    },
    "Compliance": {
        "compliance_auto": {
            "msg": "Run compliance checks or fixes, depending on the autofix "
                   "module property values.",
            "options": [
                OPT.cron,
            ],
        },
        "compliance_env": {
            "msg": "Show the environment variables set during a compliance module run.",
            "options": [
                OPT.module,
                OPT.moduleset,
            ],
        },
        "compliance_check": {
            "msg": "Run compliance checks.",
            "options": [
                OPT.attach,
                OPT.force,
                OPT.module,
                OPT.moduleset,
                OPT.ruleset_date,
            ],
        },
        "compliance_fix": {
            "msg": "Run compliance fixes.",
            "options": [
                OPT.attach,
                OPT.force,
                OPT.module,
                OPT.moduleset,
                OPT.ruleset_date,
            ],
        },
        "compliance_fixable": {
            "msg": "Verify compliance fixes prerequisites.",
            "options": [
                OPT.attach,
                OPT.force,
                OPT.module,
                OPT.moduleset,
                OPT.ruleset_date,
            ],
        },
        "compliance_list_module": {
            "msg": "List the compliance modules installed on this node.",
        },
        "compliance_show_moduleset": {
            "msg": "Show compliance rules applying to this node.",
        },
        "compliance_list_moduleset": {
            "msg": "List available compliance modulesets. Setting :opt:`--moduleset f%` "
                   "limits the resultset to modulesets matching the ``f%`` pattern.",
            "options": [
                OPT.moduleset,
            ],
        },
        "compliance_list_ruleset": {
            "msg": "List available compliance rulesets. Setting :opt:`--ruleset f%` limits "
                   "the scope to rulesets matching the ``f%`` pattern.",
            "options": [
                OPT.ruleset,
            ],
        },
        "compliance_show_ruleset": {
            "msg": "Show compliance rules applying to this node.",
        },
        "compliance_show_status": {
            "msg": "Show compliance modules status.",
        },
        "compliance_attach": {
            "msg": "Attach rulesets specified by :opt:`--ruleset` and modulesets "
                   "specified by :opt:`--moduleset` to this node. Attached modulesets "
                   "are scheduled for check or autofix.",
            "options": [
                OPT.moduleset,
                OPT.ruleset,
            ],
        },
        "compliance_detach": {
            "msg": "Detach rulesets specified by :opt:`--ruleset` and modulesets "
                   "specified by :opt:`--moduleset` from this node. Detached "
                   "modulesets are no longer scheduled for check and autofix.",
            "options": [
                OPT.moduleset,
                OPT.ruleset,
            ],
        },
    },
    "Collector management": {
        "collector_cli": {
            "msg": "Open a Command Line Interface to the collector rest API. "
                   "The CLI offers autocompletion of paths and arguments, "
                   "piping JSON data from files. "
                   "If executed as root and with no :opt:`--user`, the collector is "
                   "logged in with the node credentials.",
            "options": [
                OPT.user,
                OPT.password,
                OPT.api,
                OPT.insecure,
                OPT.config,
                OPT.refresh_api,
                OPT.save,
            ],
        },
        "collector_events": {
            "msg": "Display node events during the period specified by "
                   "--begin/--end. --end defaults to now. --begin defaults to "
                   "7 days ago.",
            "options": [
                OPT.begin,
                OPT.end,
            ],
        },
        "collector_alerts": {
            "msg": "Display the node alerts.",
        },
        "collector_checks": {
            "msg": "Display the node checks.",
        },
        "collector_disks": {
            "msg": "Display the node disks list, complete with information issued by array parser.",
        },
        "collector_list_actions": {
            "msg": "List actions on the node, whatever the service, during "
                   "the period specified by --begin/--end. --end defaults to "
                   "now. --begin defaults to 7 days ago.",
            "options": [
                OPT.begin,
                OPT.end,
            ],
        },
        "collector_ack_action": {
            "msg": "Acknowledge an action error on the node. An acknowlegment "
                   "can be completed by --author (defaults to root@nodename) "
                   "and --comment",
            "options": [
                OPT.author,
                OPT.comment,
            ],
        },
        "collector_show_actions": {
            "msg": "Show actions detailed log. A single action is specified "
                   "by --id. a range is specified by --begin/--end dates. "
                   "--end defaults to now. --begin defaults to 7 days ago.",
            "options": [
                OPT.begin,
                OPT.id,
                OPT.end,
            ],
        },
        "collector_list_nodes": {
            "msg": "Show the list of nodes matching the filterset pointed by "
                   ":opt:`--filterset`.",
        },
        "collector_list_services": {
            "msg": "Show the list of services matching the filterset pointed "
                   "by :opt:`--filterset`.",
        },
        "collector_list_filtersets": {
            "msg": "Show the list of filtersets available on the collector. "
                   "If specified, :opt:`--filterset <pattern>` limits the resultset "
                   "to filtersets matching the pattern.",
        },
        "collector_log": {
            "msg": "Log a message in the collector's node log.",
            "options": [
                OPT.message,
            ],
        },
        "collector_asset": {
            "msg": "Display the asset information known to the collector.",
        },
        "collector_networks": {
            "msg": "Display network information known to the collector for "
                   "each node ip, complete with network information from the "
                   "IPAM database.",
        },
        "collector_tag": {
            "msg": "Set a node tag (pointed by --tag).",
            "options": [
                OPT.tag,
            ],
        },
        "collector_untag": {
            "msg": "Unset a node tag (pointed by --tag).",
        },
        "collector_show_tags": {
            "msg": "list all node tags",
        },
        "collector_list_tags": {
            "msg": "List all available tags. Use :opt:`--like` to filter the output.",
            "options": [
                OPT.like,
            ],
        },
        "collector_create_tag": {
            "msg": "Create a new tag with name specified by :opt:`--tag`.",
            "options": [
                OPT.tag,
            ],
        },
        "collector_search": {
            "msg": "Report the collector objects matching :opt:`--like "
                   "[<type>:]<substring>`, where ``<type>`` is the object type "
                   "acronym as shown in the collector search widget.",
            "options": [
                OPT.like,
            ],
        },
    },
}

DEPRECATED_ACTIONS = [
    "collector_json_asset",
    "collector_json_networks",
    "collector_json_list_unavailability_ack",
    "collector_json_list_actions",
    "collector_json_show_actions",
    "collector_json_status",
    "collector_json_checks",
    "collector_json_disks",
    "collector_json_alerts",
    "collector_json_events",
    "collector_json_list_nodes",
    "collector_json_list_services",
    "collector_json_list_filtersets",
    "json_schedule",
]

ACTIONS_TRANSLATIONS = {
    "unfreeze": "thaw",
}

class NodemgrOptParser(OptParser):
    """
    The nodemgr-specific options parser class
    """
    def __init__(self, args=None, colorize=True, width=None, formatter=None,
                 indent=6):
        OptParser.__init__(self, args=args, prog=PROG, options=OPT,
                           actions=ACTIONS,
                           deprecated_actions=DEPRECATED_ACTIONS,
                           actions_translations=ACTIONS_TRANSLATIONS,
                           global_options=GLOBAL_OPTS,
                           colorize=colorize, width=width,
                           formatter=formatter, indent=indent)

   0707010005c8d7000081b40000000000000000000000015ab21754000033a0000000660000000000000000000000000000002400000000root/usr/share/opensvc/lib/osvcd.py   """
The opensvc daemon.
"""
from __future__ import print_function

import sys
import os
import threading
import logging
import codecs
from optparse import OptionParser

import rcExceptions as ex
import rcLogger
import osvcd_shared as shared
from rcConfigParser import RawConfigParser
from rcGlobalEnv import rcEnv
from rcUtilities import lazy, unset_lazy, ximport
from lock import lock, unlock

from osvcd_mon import Monitor
from osvcd_lsnr import Listener
from osvcd_scheduler import Scheduler
from osvcd_collector import Collector
from osvcd_dns import Dns
from hb_ucast import HbUcastRx, HbUcastTx
from hb_mcast import HbMcastRx, HbMcastTx
from hb_disk import HbDiskRx, HbDiskTx
from hb_relay import HbRelayRx, HbRelayTx

node_mod = ximport('node')

DAEMON_TICKER = threading.Condition()
DAEMON_INTERVAL = 2

HEARTBEATS = (
    ("multicast", HbMcastTx, HbMcastRx),
    ("unicast", HbUcastTx, HbUcastRx),
    ("disk", HbDiskTx, HbDiskRx),
    ("relay", HbRelayTx, HbRelayRx),
)

def fork(func, args=None, kwargs=None):
    """
    A fork daemonizing function.
    """
    if args is None:
        args = []
    if kwargs is None:
        kwargs = {}

    if os.fork() > 0:
        # return to parent execution
        return

    # separate the son from the father
    os.chdir('/')
    os.setsid()

    try:
        pid = os.fork()
        if pid > 0:
            os._exit(0)
    except Exception:
        os._exit(1)

    # Redirect standard file descriptors.
    if hasattr(os, "devnull"):
        devnull = os.devnull
    else:
        devnull = "/dev/null"

    for fileno in range(0, 3):
        try:
            os.close(fileno)
        except OSError:
            pass

    # Popen(close_fds=True) does not close 0, 1, 2. Make sure we have those
    # initialized to /dev/null
    os.open(devnull, os.O_RDWR)
    os.dup2(0, 1)
    os.dup2(0, 2)

    try:
        func(*args, **kwargs)
    except Exception:
        sys.exit(1)

    sys.exit(0)

def forked(func):
    """
    A decorator that runs the decorated function in a detached subprocess
    immediately. A lock is held to avoid running the same function twice.
    """
    def _func(*args, **kwargs):
        fork(func, args, kwargs)
    return _func

#############################################################################
#
# Daemon
#
#############################################################################
class Daemon(object):
    """
    The OpenSVC daemon process.
    Can run forked or foreground.
    Janitors all the listener, the monitor and all heartbeat threads.
    Monitors the node configuration file and notify its changes to threads.
    """
    def __init__(self):
        self.handlers = None
        self.threads = {}
        self.last_config_mtime = None
        rcLogger.initLogger(rcEnv.nodename, self.handlers)
        rcLogger.set_namelen(force=30)
        self.log = logging.getLogger(rcEnv.nodename+".osvcd")

    def stop(self):
        """
        The global stop method. Signal all threads to shutdown.
        """
        self.log.info("daemon stop")
        self.stop_threads()

    def run(self, daemon=True):
        """
        Switch between the forked/foreground execution mode.
        Drop the stream handler for the forked mode.
        """
        if daemon:
            self.handlers = ["file", "syslog"]
            self._run_daemon()
        else:
            self._run()

    @forked
    def _run_daemon(self):
        """
        The method used as fork-point in the daemon execution mode.
        """
        self.log.info("daemon started")
        self._run()

    def init_nodeconf(self):
        if not os.path.exists(rcEnv.paths.pathetc):
            self.log.info("create dir %s", rcEnv.paths.pathetc)
            os.makedirs(rcEnv.paths.pathetc)
        if not os.path.exists(rcEnv.paths.nodeconf):
            self.log.info("create %s", rcEnv.paths.nodeconf)
            with open(rcEnv.paths.nodeconf, "a") as ofile:
                ofile.write("")
            os.chmod(rcEnv.paths.nodeconf, 0o0600)

    @lazy
    def config(self):
        """
        Allocate a config parser object and load the node configuration.
        Abstracting python2/3 differences in the parser modules and utf8
        handling.
        """
        self.init_nodeconf()
        try:
            config = RawConfigParser()
            with codecs.open(rcEnv.paths.nodeconf, "r", "utf8") as filep:
                if sys.version_info[0] >= 3:
                    config.read_file(filep)
                else:
                    config.readfp(filep)
        except Exception as exc:
            self.log.info("error loading config: %s", exc)
            raise ex.excAbortAction()
        return config

    def lock(self):
        try:
            self.lockfd = lock(lockfile=rcEnv.paths.daemon_lock, timeout=0, delay=0)
        except Exception:
            self.log.error("a daemon is already running, and holding the daemon lock")
            sys.exit(1)

    def unlock(self):
        if self.lockfd:
            unlock(self.lockfd)

    def _run(self):
        """
        Acquire the osvcd lock, write the pid in a system-compatible pidfile,
        and start the daemon loop.
        """
        self.lock()
        try:
            self.write_pid()
            self.loop_forever()
        finally:
            self.unlock()

    def write_pid(self):
        pid = str(os.getpid())+"\n"
        with open(rcEnv.paths.daemon_pid, "w") as ofile:
            ofile.write(pid)

    def loop_forever(self):
        """
        Loop over the daemon tasks until notified to stop.
        """
        while self.loop():
            with DAEMON_TICKER:
                DAEMON_TICKER.wait(DAEMON_INTERVAL)
        self.log.info("daemon graceful stop")

    def loop(self):
        self.start_threads()
        if shared.DAEMON_STOP.is_set():
            self.stop_threads()
            return False
        return True

    def stop_threads(self):
        """
        Send a stop notification to all threads, and wait for them to
        complete their shutdown.
        Stop dns last, so the service is available as long as possible.
        """
        self.log.info("signal stop to all threads")
        for thr_id, thr in self.threads.items():
            if thr_id == "dns":
                continue
            thr.stop()
        shared.wake_collector()
        shared.wake_scheduler()
        shared.wake_monitor(reason="stop threads", immediate=True)
        shared.wake_heartbeat_tx()
        for thr_id, thr in self.threads.items():
            if thr_id == "dns":
                continue
            self.log.info("waiting for %s to stop", thr_id)
            thr.join()
        if "dns" in self.threads:
            self.threads["dns"].stop()
            self.log.info("waiting for dns to stop")
            self.threads["dns"].join()

    def need_start(self, thr_id):
        """
        Return True if a thread need restarting, ie not signalled to stop
        and not alive.
        """
        if thr_id not in self.threads:
            return True
        thr = self.threads[thr_id]
        if thr.stopped():
            return False
        if thr.is_alive():
            return False
        return True

    def start_threads(self):
        """
        Reload the node configuration if needed.
        Start threads or restart threads dead of an unexpected cause.
        Stop and delete heartbeat threads whose configuration was deleted.
        """
        config_changed = self.read_config()

        # a thread can only be started once, allocate a new one if not alive.
        changed = False
        if shared.NODE.dns and self.need_start("dns"):
            self.threads["dns"] = Dns()
            self.threads["dns"].start()
            changed = True
        if self.need_start("listener"):
            self.threads["listener"] = Listener()
            self.threads["listener"].start()
            changed = True
        if self.need_start("scheduler"):
            self.threads["scheduler"] = Scheduler()
            self.threads["scheduler"].start()
            changed = True
        if shared.NODE and rcEnv.dbopensvc and self.need_start("collector"):
            self.threads["collector"] = Collector()
            self.threads["collector"].start()
            changed = True
        if self.need_start("monitor"):
            self.threads["monitor"] = Monitor()
            self.threads["monitor"].start()
            changed = True

        for hb_type, txc, rxc in HEARTBEATS:
            for name in self.get_config_hb(hb_type):
                hb_id = name + ".rx"
                if self.need_start(hb_id):
                    self.threads[hb_id] = rxc(name)
                    self.threads[hb_id].start()
                    changed = True
                hb_id = name + ".tx"
                if self.need_start(hb_id):
                    self.threads[hb_id] = txc(name)
                    self.threads[hb_id].start()
                    changed = True

        if config_changed:
            # clean up deleted heartbeats
            thr_ids = list(self.threads.keys())
            sections = self.config.sections()
            for thr_id in thr_ids:
                if not thr_id.startswith("hb#"):
                    continue
                name = thr_id.replace(".tx", "").replace(".rx", "")
                if name in sections:
                    continue
                self.log.info("heartbeat %s removed from configuration. stop "
                              "thread %s", name, thr_id)
                self.threads[thr_id].stop()
                self.threads[thr_id].join()
                del self.threads[thr_id]
                changed = True

        if changed:
            with shared.THREADS_LOCK:
                shared.THREADS = self.threads

    def get_config_mtime(self, first=True):
        try:
            mtime = os.path.getmtime(rcEnv.paths.nodeconf)
        except Exception as exc:
            if first:
                self.init_nodeconf()
                return self.get_config_mtime(first=False)
            else:
                self.log.warning("failed to get node config mtime: %s", str(exc))
                return
        return mtime

    def read_config(self):
        """
        Reload the node configuration file and notify the threads to do the
        same, if the file's mtime has changed since the last load.
        """
        mtime = self.get_config_mtime()
        if mtime is None:
            return
        if self.last_config_mtime is not None and \
           self.last_config_mtime >= mtime:
            return
        try:
            with shared.NODE_LOCK:
                if shared.NODE:
                    shared.NODE.close()
                shared.NODE = node_mod.Node()
            unset_lazy(self, "config")
            unset_lazy(self, "config_hbs")
            if self.last_config_mtime:
                self.log.info("node config reloaded (changed)")
            else:
                self.log.info("node config loaded")
            self.last_config_mtime = mtime

            # signal the node config change to threads
            for thr_id in self.threads:
                self.threads[thr_id].notify_config_change()
            shared.wake_monitor(reason="config change", immediate=True)

            # signal the caller the config has changed
            return True
        except Exception as exc:
            self.log.warning("failed to load config: %s", str(exc))

    def get_config_hb(self, hb_type=None):
        """
        Parse the node configuration and return the list of heartbeat
        section names matching the specified type.
        """
        return self.config_hbs.get(hb_type, [])

    @lazy
    def config_hbs(self):
        """
        Parse the node configuration and return a dictionary of heartbeat
        section names indexed by heartbeat type.
        """
        hbs = {}
        for section in self.config.sections():
            if not section.startswith("hb#"):
                continue
            try:
                section_type = self.config.get(section, "type")
            except Exception:
                continue
            if section_type not in hbs:
                hbs[section_type] = [section]
            else:
                hbs[section_type].append(section)
        return hbs

#############################################################################
#
# Main
#
#############################################################################
def optparse():
    """
    Parse command line options for main().
    """
    parser = OptionParser()
    parser.add_option(
        "-f", "--foreground", action="store_false",
        default=True, dest="daemon"
    )
    return parser.parse_args()

def main():
    """
    Start the daemon and catch Exceptions to reap it down cleanly.
    """
    options, _ = optparse()
    try:
        daemon = Daemon()
        daemon.run(daemon=options.daemon)
    except (KeyboardInterrupt, ex.excSignal):
        daemon.log.info("interrupted")
        daemon.stop()
    except Exception as exc:
        daemon.log.exception(exc)
        daemon.stop()

if __name__ == "__main__":
    main()
0707010005c8df000081b40000000000000000000000015ab21754000000ea000000660000000000000000000000000000002600000000root/usr/share/opensvc/lib/provApp.py import provisioning

class Prov(provisioning.Prov):
    def __init__(self, r):
        provisioning.Prov.__init__(self, r)
    
    def start(self):
        """
        Never start app resources on provision
        """
        pass

  0707010005c8d8000081b40000000000000000000000015ab2175400001aa6000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/osvcd_collector.py """
Collector Thread
"""
import os
import sys
import logging
import datetime

import osvcd_shared as shared
import rcExceptions as ex
from comm import Crypt
from rcGlobalEnv import rcEnv

class Collector(shared.OsvcThread, Crypt):
    interval = 300

    def run(self):
        self.log = logging.getLogger(rcEnv.nodename+".osvcd.collector")
        self.log.info("collector started")
        self.last_comm = None
        self.last_config = {}
        self.last_status = {}

        while True:
            if self.stopped():
                sys.exit(0)
            try:
                self.do()
            except Exception as exc:
                self.log.exception(exc)

    def get_last_status(self, data):
        last_status = {}
        last_status_changed = []
        for nodename, ndata in data["nodes"].items():
            for svcname, sdata in ndata.get("services", {}).get("status", {}).items():
                status_csum = sdata.get("csum")
                prev_status_csum = self.last_status.get((svcname, nodename))
                if status_csum != prev_status_csum:
                    last_status_changed.append(svcname+"@"+nodename)
                    if svcname not in last_status_changed:
                        last_status_changed.append(svcname)
                last_status[(svcname, nodename)] = status_csum
        return last_status, last_status_changed

    def get_last_config(self, data):
        last_config = {}
        last_config_changed = []
        for svcname, sdata in data["nodes"].get(rcEnv.nodename, {}).get("services", {}).get("config", {}).items():
            config_csum = sdata.get("csum")
            prev_config_csum = self.last_config.get(svcname)
            if config_csum != prev_config_csum:
                last_config_changed.append(svcname)
            last_config[svcname] = config_csum
        return last_config, last_config_changed

    def init_collector(self):
        if " 127.0.0.1/" in repr(shared.NODE.collector.proxy):
            shared.NODE.collector.init()

    def do(self):
        self.reload_config()
        self.init_collector()
        self.run_collector()
        self.unqueue_xmlrpc()
        with shared.COLLECTOR_TICKER:
            shared.COLLECTOR_TICKER.wait(self.interval)

    def unqueue_xmlrpc(self):
        while True:
            try:
                args, kwargs = shared.COLLECTOR_XMLRPC_QUEUE.pop()
            except IndexError:
                break
            if len(args) == 0:
                continue
            try:
                #self.log.info("call %s", args[0])
                shared.NODE.collector.call(*args, **kwargs)
            except Exception as exc:
                self.log.exception(exc)

    def send_containerinfo(self, svcname):
        if svcname not in shared.SERVICES:
            return
        if not shared.SERVICES[svcname].has_encap_resources:
            return
        self.log.info("send service %s container info", svcname)
        with shared.SERVICES_LOCK:
            shared.NODE.collector.call("push_containerinfo", shared.SERVICES[svcname])

    def send_service_config(self, svcname):
        if svcname not in shared.SERVICES:
            return
        self.log.info("send service %s config", svcname)
        with shared.SERVICES_LOCK:
            shared.NODE.collector.call("push_config", shared.SERVICES[svcname])

    def send_daemon_status(self, data, last_status_changed=None):
        if last_status_changed:
            self.log.info("send daemon status, changed: %s", ", ".join(last_status_changed))
        else:
            self.log.info("send daemon status, resync")
        shared.NODE.collector.call("push_daemon_status", data, last_status_changed)
        self.last_comm = datetime.datetime.utcnow()

    def ping(self):
        self.log.info("ping the collector")
        shared.NODE.collector.call("daemon_ping")
        self.last_comm = datetime.datetime.utcnow()

    def speaker(self):
        for nodename in self.sorted_cluster_nodes:
            if nodename in shared.CLUSTER_DATA and shared.CLUSTER_DATA[nodename] != "unknown":
                break
        if nodename == rcEnv.nodename:
            #self.log.debug("we are speaker", nodename)
            return True
        #self.log.debug("the speaker is %s", nodename)
        return False

    def get_data(self):
        """
        Get a copy of the monitor thread, expunged from encap services,
        to avoid missing changes happening during our work
        """
        if "monitor" not in shared.THREADS:
            # the monitor thread is not started
            return
        data = shared.THREADS["monitor"].status()
        _data = {
            "nodes": {},
            "services": {},
        }
        for key in data:
            if key not in _data:
                _data[key] = data[key]

        for nodename in data["nodes"]:
            try:
                instances_status = data["nodes"][nodename]["services"]["status"]
                instances_config = data["nodes"][nodename]["services"]["config"]
            except KeyError:
                continue
            for svcname in list(instances_status.keys()):
                if svcname not in instances_config:
                    # deleted service instance
                    continue
                if instances_status[svcname].get("encap") is True:
                    continue
                if nodename not in _data["nodes"]:
                    _data["nodes"][nodename] = {
                        "services": {
                            "config": {},
                            "status": {},
                        },
                    }
                _data["nodes"][nodename]["services"]["status"][svcname] = instances_status[svcname]
                _data["nodes"][nodename]["services"]["config"][svcname] = instances_config[svcname]
                _data["services"][svcname] = data["services"][svcname]
        return _data

    def run_collector(self):
        data = self.get_data()
        if data is None:
            return
        if len(data["services"]) == 0:
            #self.log.debug("no service")
            return

        last_config, last_config_changed = self.get_last_config(data)
        for svcname in last_config_changed:
            self.send_service_config(svcname)
            self.send_containerinfo(svcname)
        self.last_config = last_config

        if self.speaker():
            last_status, last_status_changed = self.get_last_status(data)
            if last_status_changed != [] or self.last_comm is None:
                self.send_daemon_status(data, last_status_changed)
            elif self.last_comm <= datetime.datetime.utcnow() - datetime.timedelta(seconds=self.interval):
                self.ping()
            self.last_status = last_status


  0707010005c8d9000081b40000000000000000000000015ab21754000039be000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/osvcd_dns.py   """
Listener Thread
"""
import os
import sys
import socket
import logging
import threading
import time
import shutil
import json
import re
import hashlib

import osvcd_shared as shared
from rcGlobalEnv import rcEnv, Storage
from rcUtilities import lazy, bdecode
from comm import Crypt

PTR_SUFFIX = ".in-addr.arpa."
PTR_SUFFIX_LEN = 14
PTR6_SUFFIX = ".ip6.arpa."
PTR6_SUFFIX_LEN = 10

if sys.version_info[0] < 3:
    MAKEFILE_KWARGS = {"bufsize": 0}
else:
    MAKEFILE_KWARGS = {"buffering": None}

class Dns(shared.OsvcThread, Crypt):
    sock_tmo = 1.0

    def run(self):
        self.log = logging.getLogger(rcEnv.nodename+".osvcd.dns")
        if not os.path.exists(rcEnv.paths.dnsuxsockd):
            os.makedirs(rcEnv.paths.dnsuxsockd)
        try:
            if os.path.isdir(rcEnv.paths.dnsuxsock):
                shutil.rmtree(rcEnv.paths.dnsuxsock)
            else:
                os.unlink(rcEnv.paths.dnsuxsock)
        except Exception:
            pass
        try:
            self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
            self.sock.bind(rcEnv.paths.dnsuxsock)
            self.sock.listen(1)
            self.sock.settimeout(self.sock_tmo)
        except socket.error as exc:
            self.log.error("bind %s error: %s", rcEnv.paths.dnsuxsock, exc)
            return

        self.log.info("listening on %s", rcEnv.paths.dnsuxsock)

        self.suffix = ".%s." % self.cluster_name
        self.suffix_len = len(self.suffix)
        self.soa_data = {
            "origin": self.origin,
            "contact": self.contact,
            "serial": 1,
            "refresh": 7200,
            "retry": 3600,
            "expire": 432000,
            "minimum": 86400,
        }
        self.soa_content = "%(origin)s %(contact)s %(serial)d %(refresh)d " \
                           "%(retry)d %(expire)d %(minimum)d" % self.soa_data

        self.stats = Storage({
            "sessions": Storage({
                "accepted": 0,
                "tx": 0,
                "rx": 0,
            }),
        })

        while True:
            try:
                self.do()
            except Exception as exc:
                self.log.exception(exc)
            if self.stopped():
                self.log.info("stop event received (%d handler threads to join)", len(self.threads))
                self.join_threads()
                self.sock.close()
                sys.exit(0)

    def status(self, **kwargs):
        data = shared.OsvcThread.status(self, **kwargs)
        if hasattr(self, "stats"):
            data["stats"] = self.stats
        return data

    def do(self):
        self.reload_config()
        self.janitor_procs()
        self.janitor_threads()

        try:
            conn, addr = self.sock.accept()
            #self.log.info("accept connection")
            self.stats.sessions.accepted += 1
        except socket.timeout:
            return
        thr = threading.Thread(target=self.handle_client, args=(conn,))
        thr.start()
        self.threads.append(thr)

    def handle_client(self, conn):
        conn.settimeout(self.sock_tmo)
        cr = conn.makefile("r", **MAKEFILE_KWARGS)
        cw = conn.makefile("w", **MAKEFILE_KWARGS)
        try:
            self._handle_client(conn, cr, cw)
        except Exception as exc:
            self.log.exception(exc)
        finally:
            try:
                cr.close()
            except socket.error:
                pass
            try:
                cw.close()
            except socket.error:
                pass
            try:
                conn.close()
            except socket.error:
                pass
            sys.exit(0)

    def _handle_client(self, conn, cr, cw):
        chunks = []
        buff_size = 4096
        while True:
            try:
                data = cr.readline()
            except socket.timeout as exc:
                break
            except socket.error as exc:
                self.log.info("%s", exc)
                break
            if len(data) == 0:
                #self.log.info("no more data")
                break

            self.log.debug("received %s", data)
    
            try:
                data = bdecode(data)
                data = json.loads(data)
            except Exception as exc:
                self.log.error(exc)
                data = None
    
            if self.stopped():
                self.log.info("stop event received (handler thread)")
                break

            if data is None or not isinstance(data, dict):
                continue

            result = self.router(data)
            if result is not None:
                message = json.dumps(result) + "\n"
                cw.write(message)
                cw.flush()
                self.log.debug("replied %s", message)
                message_len = len(message)
                self.stats.sessions.tx += message_len

    #########################################################################
    #
    # Methods
    #
    #########################################################################
    def router(self, data):
        """
        For a request data, extract the requested action and options,
        translate into a method name, and execute this method with options
        passed as keyword args.
        """
        if not isinstance(data, dict):
            return {"error": "invalid data format", "result": False}
        if "method" not in data:
            return {"error": "method not specified", "result": False}
        fname = "action_"+data["method"]
        if not hasattr(self, fname):
            return {"error": "action not supported", "result": False}
        result = getattr(self, fname)(data.get("parameters", {}))
        return {"result": result}

    def action_initialize(self, parameters):
        return True

    def action_getDomainMetadata(self, parameters):
        """
        {
            "method": "getDomainMetadata",
            "parameters": {
                "kind": "ALLOW-AXFR-FROM",
                "name": "svcdevops-front.default."
            }
        }
        """
        kind = parameters.get("kind")
        if kind == "ALLOW-AXFR-FROM":
            return ["0.0.0.0/0", "AUTO-NS"]
        if kind == "PRESIGNED":
            return ["1"]
        return ["0"]

    def action_lookup(self, parameters):
        qtype = parameters.get("qtype")
        qname = parameters.get("qname")
        #zone_id = parameters.get("zone-id")

        if qtype == "SOA":
            return self.soa_record(parameters)
        if qtype == "A":
            return self.a_record(parameters)
        if qtype == "SRV":
            return self.srv_record(parameters)
        if qtype == "TXT":
            return self.txt_record(parameters)
        if qtype == "PTR":
            return self.ptr_record(parameters)
        if qtype == "CNAME":
            return self.cname_record(parameters)
        if qtype == "ANY":
            if PTR_SUFFIX in qname:
                return self.ptr_record(parameters)
            return self.a_record(parameters) + \
                   self.srv_record(parameters) + \
                   self.txt_record(parameters) + \
                   self.cname_record(parameters)
        return []

    def remove_suffix(self, qname):
        return qname[:-self.suffix_len]

    @lazy
    def origin(self):
        return "dns"+self.suffix

    @lazy
    def contact(self):
        return "contact@opensvc.com"

    def soa_record(self, parameters):
        qname = parameters.get("qname")
        if qname.endswith(PTR_SUFFIX):
            if qname not in self.soa_records_rev():
                return []
        elif qname.endswith(self.suffix):
            if qname not in self.soa_records():
                return []
        else:
            return []

        data = [
            {
                "qtype": "SOA",
                "qname": qname,
                "content": self.soa_content,
                "ttl": 60,
                "domain_id": -1
            }
        ]
        return data

    def cname_record(self, parameters):
        return []

    def txt_record(self, parameters):
        return []

    def srv_record(self, parameters):
        qname = parameters.get("qname")
        if not qname.endswith(self.suffix):
            return []
        return [{
            "qtype": "SRV",
            "qname": qname,
            "content": content,
            "ttl": 60
        } for content in self.srv_records().get(qname, [])]

    def ptr_record(self, parameters):
        qname = parameters.get("qname")
        return [{
            "qtype": "PTR",
            "qname": qname,
            "content": name,
            "ttl": 60
        } for name in self.svc_ptr_record(qname)]

    def a_record(self, parameters):
        qname = parameters.get("qname")
        if not qname.endswith(self.suffix):
            return []
        return [{
            "qtype": "A",
            "qname": qname,
            "content": addr,
            "ttl": 60
        } for addr in self.a_records().get(qname, [])]

    def svc_ptr_record(self, qname):
        if not qname.endswith(PTR_SUFFIX):
            return []
        names = []
        ref = ".".join(reversed(qname[:-PTR_SUFFIX_LEN].split(".")))
        for nodename, node in shared.CLUSTER_DATA.items():
            status = node.get("services", {}).get("status", {})
            for svcname, svc in status.items():
                app = svc.get("app", "default").lower()
                for rid, resource in status[svcname].get("resources", {}).items():
                    addr = resource.get("info", {}).get("ipaddr")
                    if addr is None:
                        continue
                    if addr != ref:
                        continue
                    if "hostname" in resource:
                        names.append("%s.%s.%s.svc.%s." % (hostname, svcname, app, self.cluster_name))
                    names.append("%s.%s.svc.%s." % (svcname, app, self.cluster_name))
        return names

    def soa_records(self):
        names = set([self.suffix])
        for nodename, node in shared.CLUSTER_DATA.items():
            status = node.get("services", {}).get("status", {})
            for svcname, svc in status.items():
                scaler_slave = svc.get("scaler_slave")
                if scaler_slave:
                    continue
                app = svc.get("app", "default").lower()
                names.add("%s.%s.svc.%s." % (svcname, app, self.cluster_name))
                names.add("%s.svc.%s." % (app, self.cluster_name))
        return names

    @staticmethod
    def unique_name(addr):
        return hashlib.sha1(addr.encode("ascii")).hexdigest()

    def a_records(self):
        names = {}
        for nodename, node in shared.CLUSTER_DATA.items():
            status = node.get("services", {}).get("status", {})
            for svcname, svc in status.items():
                app = svc.get("app", "default").lower()
                scaler_slave = svc.get("scaler_slave")
                if scaler_slave:
                    _svcname = svcname[svcname.index(".")+1:]
                else:
                    _svcname = svcname
                qname = "%s.%s.svc.%s." % (_svcname, app, self.cluster_name)
                if qname not in names:
                    names[qname] = set()
                for rid, resource in status[svcname].get("resources", {}).items():
                    addr = resource.get("info", {}).get("ipaddr")
                    if addr is None:
                        continue
                    hostname = resource.get("info", {}).get("hostname")
                    names[qname].add(addr)
                    rname = self.unique_name(addr) + "." + qname
                    if rname not in names:
                        names[rname] = set()
                    names[rname].add(addr)
                    if hostname:
                        name = hostname + "." + qname
                        if name not in names:
                            names[name] = set()
                        names[name].add(addr)
        return names

    def srv_records(self):
        names = {}
        for nodename, node in shared.CLUSTER_DATA.items():
            status = node.get("services", {}).get("status", {})
            weight = node.get("stats", {}).get("score", 10)
            for svcname, svc in status.items():
                app = svc.get("app", "default").lower()
                scaler_slave = svc.get("scaler_slave")
                if scaler_slave:
                    _svcname = svcname[svcname.index(".")+1:]
                else:
                    _svcname = svcname
                for rid, resource in status[svcname].get("resources", {}).items():
                    addr = resource.get("info", {}).get("ipaddr")
                    if addr is None:
                        continue
                    for expose in resource.get("info", {}).get("expose", []):
                        try:
                            port, proto = re.split("[/-]", expose.split(":")[0])
                            port = int(port)
                        except Exception as exc:
                            continue
                        try:
                            serv = socket.getservbyport(port)
                        except socket.error as exc:
                            continue
                        qname = "_%s._%s.%s.%s.svc.%s." % (serv, proto, _svcname, app, self.cluster_name)
                        target = "%s.%s.%s.svc.%s." % (self.unique_name(addr), _svcname, app, self.cluster_name)
                        if qname not in names:
                            names[qname] = set()
                        content = "%(prio)d %(weight)d %(port)d %(target)s" % {
                            "prio": 0,
                            "weight": weight,
                            "port": port,
                            "target": target,
                        }
                        names[qname].add(content)
        return names

    def svc_ips(self):
        addrs = []
        for nodename, node in shared.CLUSTER_DATA.items():
            status = node.get("services", {}).get("status", {})
            for svcname, svc in status.items():
                for rid, resource in status[svcname].get("resources", {}).items():
                    addr = resource.get("info", {}).get("ipaddr")
                    if addr is None:
                        continue
                    addrs.append(addr)
        return addrs

    def soa_records_rev(self):
        addrs = []
        for addr in set(self.svc_ips()):
            addrs.append(".".join(reversed(addr.split(".")[:-1]))+PTR_SUFFIX)
        return addrs

  0707010005c8da000081b40000000000000000000000015ab217540000694f000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/osvcd_lsnr.py  """
Listener Thread
"""
import os
import sys
import socket
import logging
import threading
import codecs
import time
import select
from subprocess import Popen, PIPE

try:
    import Queue as queue
except ImportError:
    import queue

import osvcd_shared as shared
from rcGlobalEnv import rcEnv, Storage
from rcUtilities import bdecode, drop_option
from converters import convert_size
from comm import Crypt

RELAY_DATA = {}
RELAY_LOCK = threading.RLock()

class Listener(shared.OsvcThread, Crypt):
    sock_tmo = 1.0

    def setup_sock(self):
        try:
            self.port = self.config.getint("listener", "port")
        except Exception:
            self.port = rcEnv.listener_port
        try:
            self.addr = self.config.get("listener", "addr")
        except Exception:
            self.addr = "0.0.0.0"

        try:
            addrinfo = socket.getaddrinfo(self.addr, None)[0]
            self.addr = addrinfo[4][0]
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.sock.bind((self.addr, self.port))
            self.sock.listen(5)
            self.sock.settimeout(self.sock_tmo)
        except socket.error as exc:
            self.log.error("bind %s:%d error: %s", self.addr, self.port, exc)
            return
        self.log.info("listening on %s:%s", self.addr, self.port)
        self.sockmap[self.sock.fileno()] = self.sock

    def setup_sockux(self):
        if os.name == "nt":
            return
        if not os.path.exists(rcEnv.paths.lsnruxsockd):
            os.makedirs(rcEnv.paths.lsnruxsockd)
        try:
            if os.path.isdir(rcEnv.paths.lsnruxsock):
                shutil.rmtree(rcEnv.paths.lsnruxsock)
            else:
                os.unlink(rcEnv.paths.lsnruxsock)
        except Exception:
            pass
        try:
            self.sockux = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
            self.sockux.bind(rcEnv.paths.lsnruxsock)
            self.sockux.listen(1)
            self.sockux.settimeout(self.sock_tmo)
        except socket.error as exc:
            self.log.error("bind %s error: %s", rcEnv.paths.lsnruxsock, exc)
            return
        self.log.info("listening on %s", rcEnv.paths.dnsuxsock)
        self.sockmap[self.sockux.fileno()] = self.sockux

    def run(self):
        self.log = logging.getLogger(rcEnv.nodename+".osvcd.listener")
        self.events_clients = []
        self.stats = Storage({
            "sessions": Storage({
                "accepted": 0,
                "auth_validated": 0,
                "tx": 0,
                "rx": 0,
                "clients": Storage({
                })
            }),
        })
        self.sockmap = {}
        self.setup_sock()
        self.setup_sockux()

        while True:
            try:
                self.do()
            except Exception as exc:
                self.log.exception(exc)
            if self.stopped():
                self.join_threads()
                self.sock.close()
                sys.exit(0)

    def status(self, **kwargs):
        data = shared.OsvcThread.status(self, **kwargs)
        data["stats"] = self.stats
        data["config"] = {
            "port": self.port,
            "addr": self.addr,
        }
        return data

    def do(self):
        self.reload_config()
        self.janitor_procs()
        self.janitor_threads()
        self.janitor_events()

        fds = select.select([fno for fno in self.sockmap], [], [], self.sock_tmo)
        if self.sock_tmo and fds == ([], [], []):
            return
        for fd in fds[0]:
            sock = self.sockmap[fd]
            try:
                conn, addr = sock.accept()
                self.stats.sessions.accepted += 1
                if len(addr) == 0:
                    addr = ["local"]
                    encrypted = False
                else:
                    encrypted = True
                if addr[0] not in self.stats.sessions.clients:
                    self.stats.sessions.clients[addr[0]] = Storage({
                        "accepted": 0,
                        "auth_validated": 0,
                        "tx": 0,
                        "rx": 0,
                    })
                self.stats.sessions.clients[addr[0]].accepted += 1
                #self.log.info("accept %s", str(addr))
            except socket.timeout:
                return
            thr = threading.Thread(target=self.handle_client, args=(conn, addr, encrypted))
            thr.start()
            self.threads.append(thr)

    def janitor_events(self):
        done = []
        while True:
            try:
                event = shared.EVENT_Q.get(False, 0)
            except queue.Empty:
                break
            emsg = self.encrypt(event)
            msg = self.msg_encode(event)
            to_remove = []
            for idx, (conn, encrypted) in enumerate(self.events_clients):
                if encrypted:
                    _msg = emsg
                else:
                    _msg = msg
                try:
                    conn.sendall(_msg)
                except socket.error as exc:
                    to_remove.append(idx)
            for idx in to_remove:
                try:
                    self.events_clients[idx][0].close()
                except Exception:
                    pass
                try:
                    del self.events_clients[idx]
                except IndexError:
                    pass

    def handle_client(self, conn, addr, encrypted):
        try:
            self._handle_client(conn, addr, encrypted)
        finally:
            conn.close()

    def _handle_client(self, conn, addr, encrypted):
        chunks = []
        buff_size = 4096
        conn.setblocking(0)
        while True:
            ready = select.select([conn], [], [conn], 6)
            if ready[0]:
                chunk = conn.recv(buff_size)
            else:
                self.log.warning("timeout waiting for data from client %s", addr[0])
                break
            if ready[2]:
                self.log.debug("exceptional condition on socket with client %s", addr[0])
                break
            self.stats.sessions.rx += len(chunk)
            self.stats.sessions.clients[addr[0]].rx += len(chunk)
            if chunk:
                chunks.append(chunk)
            if not chunk or chunk.endswith(b"\x00"):
                break
        if sys.version_info[0] >= 3:
            data = b"".join(chunks)
            dequ = data == b"dequeue_actions"
        else:
            data = "".join(chunks)
            dequ = data == "dequeue_actions"
        del chunks

        if dequ:
            self.log.info("%s", os.getcwd())
            p = Popen([rcEnv.paths.nodemgr, 'dequeue_actions'],
                      stdout=None, stderr=None, stdin=None,
                      close_fds=os.name!="nt")
            p.communicate()
            return

        if encrypted:
            nodename, data = self.decrypt(data, sender_id=addr[0])
        else:
            try:
                data = self.msg_decode(data)
            except ValueError:
                pass
            nodename = rcEnv.nodename
        #self.log.info("received %s from %s", str(data), nodename)
        self.stats.sessions.auth_validated += 1
        self.stats.sessions.clients[addr[0]].auth_validated += 1
        if data is None:
            return
        result = self.router(nodename, data, conn, encrypted)
        if result:
            if encrypted:
                message = self.encrypt(result)
            else:
                message = self.msg_encode(result)
            conn.sendall(message)
            message_len = len(message)
            self.stats.sessions.tx += message_len
            self.stats.sessions.clients[addr[0]].tx += message_len

    #########################################################################
    #
    # Actions
    #
    #########################################################################
    def router(self, nodename, data, conn, encrypted):
        """
        For a request data, extract the requested action and options,
        translate into a method name, and execute this method with options
        passed as keyword args.
        """
        if not isinstance(data, dict):
            return {"error": "invalid data format", "status": 1}
        if "action" not in data:
            return {"error": "action not specified", "status": 1}
        fname = "action_"+data["action"]
        if not hasattr(self, fname):
            return {"error": "action not supported", "status": 1}
        # prepare options, sanitized for use as keywords
        options = {}
        for key, val in data.get("options", {}).items():
            options[str(key)] = val
        return getattr(self, fname)(nodename, conn=conn, encrypted=encrypted, **options)

    def action_relay_tx(self, nodename, **kwargs):
        with RELAY_LOCK:
            RELAY_DATA[nodename] = {
                "msg": kwargs.get("msg"),
                "updated": time.time(),
            }
        return {"status": 0}

    def action_relay_rx(self, nodename, **kwargs):
        _nodename = kwargs.get("slot")
        with RELAY_LOCK:
            if _nodename not in RELAY_DATA:
                return {"status": 1, "error": "no data"}
            return {
                "status": 0,
                "data": RELAY_DATA[_nodename]["msg"],
                "updated": RELAY_DATA[_nodename]["updated"],
            }

    def action_daemon_blacklist_clear(self, nodename, **kwargs):
        """
        Clear the senders blacklist.
        """
        self.blacklist_clear()
        return {"status": 0}

    def action_daemon_blacklist_status(self, nodename, **kwargs):
        """
        Return the senders blacklist.
        """
        return {"status": 0, "data": self.get_blacklist()}

    def action_daemon_status(self, nodename, **kwargs):
        """
        Return a hash indexed by thead id, containing the status data
        structure of each thread.
        """
        data = {
            "cluster": {
                "name": self.cluster_name,
                "id": self.cluster_id,
            }
        }
        with shared.THREADS_LOCK:
            for thr_id, thread in shared.THREADS.items():
                data[thr_id] = thread.status(**kwargs)
        return data

    def action_daemon_shutdown(self, nodename, **kwargs):
        self.log.info("shutdown daemon requested")
        with shared.THREADS_LOCK:
            shared.THREADS["monitor"].shutdown()

        while True:
            with shared.THREADS_LOCK:
                if not shared.THREADS["monitor"].is_alive():
                    break
        shared.DAEMON_STOP.set()
        return {"status": 0}

    def action_daemon_stop(self, nodename, **kwargs):
        thr_id = kwargs.get("thr_id")
        if not thr_id:
            self.log.info("stop daemon requested")
            if kwargs.get("upgrade"):
                self.set_nmon(status="upgrade")
                self.log.info("announce upgrade state")
            else:
                self.set_nmon(status="maintenance")
                self.log.info("announce maintenance state")
            time.sleep(5)
            shared.DAEMON_STOP.set()
            return {"status": 0}
        elif thr_id == "tx":
            thr_ids = [thr_id for thr_id in shared.THREADS.keys() if thr_id.endswith("tx")]
        else:
            thr_ids = [thr_id]
        for thr_id in thr_ids:
            with shared.THREADS_LOCK:
                has_thr = thr_id in shared.THREADS
            if not has_thr:
                self.log.info("stop thread requested on non-existing thread")
                return {"error": "thread does not exist"*50, "status": 1}
            self.log.info("stop thread %s requested", thr_id)
            with shared.THREADS_LOCK:
                shared.THREADS[thr_id].stop()
            if thr_id == "scheduler":
                shared.wake_scheduler()
            elif thr_id == "monitor":
                shared.wake_monitor("shutdown")
            elif thr_id.endswith("tx"):
                shared.wake_heartbeat_tx()
            if kwargs.get("wait", False):
                with shared.THREADS_LOCK:
                    shared.THREADS[thr_id].join()
        return {"status": 0}

    def action_daemon_start(self, nodename, **kwargs):
        thr_id = kwargs.get("thr_id")
        if not thr_id:
            return {"error": "no thread specified", "status": 1}
        with shared.THREADS_LOCK:
            has_thr = thr_id in shared.THREADS
        if not has_thr:
            self.log.info("start thread requested on non-existing thread")
            return {"error": "thread does not exist"*50, "status": 1}
        self.log.info("start thread requested")
        with shared.THREADS_LOCK:
            shared.THREADS[thr_id].unstop()
        return {"status": 0}

    def action_get_service_config(self, nodename, **kwargs):
        fmt = kwargs.get("format")
        if fmt == "json":
            return self._action_get_service_config_json(nodename, **kwargs)
        else:
            return self._action_get_service_config_file(nodename, **kwargs)

    def _action_get_service_config_json(self, nodename, **kwargs):
        svcname = kwargs.get("svcname")
        evaluate = kwargs.get("evaluate")
        impersonate = kwargs.get("impersonate")
        try:
            return shared.SERVICES[svcname].print_config_data(evaluate=evaluate, impersonate=impersonate)
        except Exception:
            return {}

    def _action_get_service_config_file(self, nodename, **kwargs):
        svcname = kwargs.get("svcname")
        if not svcname:
            return {"error": "no svcname specified", "status": 1}
        if shared.SMON_DATA.get(svcname, {}).get("status") in ("purging", "deleting") or \
           shared.SMON_DATA.get(svcname, {}).get("global_expect") in ("purged", "deleted"):
            return {"error": "delete in progress", "status": 2}
        fpath = os.path.join(rcEnv.paths.pathetc, svcname+".conf")
        if not os.path.exists(fpath):
            return {"error": "%s does not exist" % fpath, "status": 3}
        mtime = os.path.getmtime(fpath)
        with codecs.open(fpath, "r", "utf8") as filep:
            buff = filep.read()
        self.log.info("serve service %s config to %s", svcname, nodename)
        return {"status": 0, "data": buff, "mtime": mtime}

    def action_wake_monitor(self, nodename, **kwargs):
        svcname = kwargs.get("svcname", "<unspecified>")
        shared.wake_monitor(reason="service %s notification" % svcname)
        return {"status": 0}

    def action_clear(self, nodename, **kwargs):
        svcname = kwargs.get("svcname")
        if svcname is None:
            return {"error": "no svcname specified", "status": 1}
        self.set_smon(svcname, status="idle", reset_retries=True)
        return {"status": 0}

    def get_service_slaves(self, svcname):
        """
        Recursive lookup of service slaves.
        """
        slaves = set()
        for nodename in shared.CLUSTER_DATA:
            try:
                data = shared.CLUSTER_DATA[nodename]["services"]["status"][svcname]
            except KeyError:
                continue
            if not data.get("enslave_children"):
                continue
            _slaves = set(data.get("children", [])) - slaves
            slaves |= _slaves
            for slave in _slaves:
                slaves |= self.get_service_slaves(slave)
        return slaves

    def action_set_service_monitor(self, nodename, **kwargs):
        svcname = kwargs.get("svcname")
        if svcname is None:
            return {"error": "no svcname specified", "status": 1}
        status = kwargs.get("status")
        local_expect = kwargs.get("local_expect")
        global_expect = kwargs.get("global_expect")
        reset_retries = kwargs.get("reset_retries", False)
        stonith = kwargs.get("stonith")
        svcnames = set([svcname])
        if global_expect != "scaled":
            svcnames |= self.get_service_slaves(svcname)
        for svcname in svcnames:
            self.set_smon(
                svcname, status=status,
                local_expect=local_expect, global_expect=global_expect,
                reset_retries=reset_retries,
                stonith=stonith,
            )
        return {"status": 0}

    def action_set_node_monitor(self, nodename, **kwargs):
        status = kwargs.get("status")
        local_expect = kwargs.get("local_expect")
        global_expect = kwargs.get("global_expect")
        self.set_nmon(
            status=status,
            local_expect=local_expect, global_expect=global_expect,
        )
        return {"status": 0}

    def action_leave(self, nodename, **kwargs):
        if nodename not in self.cluster_nodes:
            self.log.info("node %s already left", nodename)
            return {"status": 0}
        ret = self.remove_cluster_node(nodename)
        result = {
            "status": ret,
        }
        return result

    def action_collector_xmlrpc(self, nodename, **kwargs):
        args = kwargs.get("args", [])
        kwargs = kwargs.get("kwargs", {})
        shared.COLLECTOR_XMLRPC_QUEUE.insert(0, (args, kwargs))
        result = {
            "status": 0,
        }
        return result

    def action_join(self, nodename, **kwargs):
        if nodename in self.cluster_nodes:
            new_nodes = self.cluster_nodes
            self.log.info("node %s rejoins", nodename)
        else:
            new_nodes = self.cluster_nodes + [nodename]
            self.add_cluster_node(nodename)
        result = {
            "status": 0,
            "data": {
                "node": {
                    "env": rcEnv.node_env,
                },
                "cluster": {
                    "nodes": new_nodes,
                    "drpnodes": self.cluster_drpnodes,
                    "id": self.cluster_id,
                    "name": self.cluster_name,
                    "quorum": self.quorum,
                },
            },
        }
        for section in self.config.sections():
            if section.startswith("hb#") or \
               section.startswith("stonith#") or \
               section.startswith("arbitrator#"):
                result["data"][section] = {}
                for key, val in self.config.items(section):
                    result["data"][section][key] = val
        return result

    def action_node_action(self, nodename, **kwargs):
        """
        Execute a nodemgr command on behalf of a peer node.
        kwargs:
        * cmd: list
        * sync: boolean
        """
        sync = kwargs.get("sync", True)
        action_mode = kwargs.get("action_mode", True)
        cmd = kwargs.get("cmd")
        if cmd is None or len(cmd) == 0:
            self.log.error("node %s requested a peer node action without "
                           "specifying the command", nodename)
            return {
                "status": 1,
            }

        cmd = drop_option("--node", cmd, drop_value=True)
        cmd = drop_option("--daemon", cmd)
        cmd = [rcEnv.paths.nodemgr] + cmd
        if action_mode and "--local" not in cmd:
            cmd += ["--local"]
        self.log.info("execute node action requested by node %s: %s",
                      nodename, " ".join(cmd))
        if sync:
            proc = Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=None, close_fds=True)
            out, err = proc.communicate()
            result = {
                "status": 0,
                "data": {
                    "out": bdecode(out),
                    "err": bdecode(err),
                    "ret": proc.returncode,
                },
            }
        else:
            proc = Popen(cmd, stdin=None, close_fds=True)
            self.push_proc(proc)
            result = {
                "status": 0,
            }
        return result

    def action_service_action(self, nodename, **kwargs):
        """
        Execute a CRM command on behalf of a peer node.
        kwargs:
        * svcname: str
        * cmd: list
        * sync: boolean
        """
        sync = kwargs.get("sync", True)
        action_mode = kwargs.get("action_mode", True)
        svcname = kwargs.get("svcname")
        if svcname is None:
            self.log.error("node %s requested a service action without "
                           "specifying the service name", nodename)
            return {
                "status": 1,
            }
        cmd = kwargs.get("cmd")
        if self.get_service(svcname) is None and "create" not in cmd:
            self.log.warning("discard service action '%s' on a service "
                             "not installed: %s", " ".join(cmd), svcname)
            return {
                "err": "service not found",
                "status": 1,
            }
        if cmd is None or len(cmd) == 0:
            self.log.error("node %s requested a service action without "
                           "specifying the command", nodename)
            return {
                "status": 1,
            }

        cmd = drop_option("--node", cmd, drop_value=True)
        cmd = drop_option("--daemon", cmd)
        if action_mode and "--local" not in cmd:
            cmd.append("--local")
        cmd = [rcEnv.paths.svcmgr, "-s", svcname] + cmd
        self.log.info("execute service action requested by node %s: %s",
                      nodename, " ".join(cmd))
        if sync:
            proc = Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=None, close_fds=True)
            out, err = proc.communicate()
            result = {
                "status": 0,
                "data": {
                    "out": bdecode(out),
                    "err": bdecode(err),
                    "ret": proc.returncode,
                },
            }
        else:
            proc = Popen(cmd, stdin=None, close_fds=True)
            self.push_proc(proc)
            result = {
                "status": 0,
            }
        return result

    def action_events(self, nodename, **kwargs):
        encrypted = kwargs.get("encrypted")
        self.events_clients.append((kwargs.get("conn").dup(), encrypted))

    def action_service_logs(self, nodename, **kwargs):
        """
        Send service logs.
        kwargs:
        * svcname
        * conn: the connexion socket to the requester
        * backlog: the number of bytes to send from the tail default is 10k.
                   A negative value means send the whole file.
                   The 0 value means follow the file.
        """
        svcname = kwargs.get("svcname")
        if svcname is None:
            return {
                "status": 1,
            }
        debug = "debug." if kwargs.get("debug") else ""
        logfile = os.path.join(rcEnv.paths.pathlog, svcname+".%slog"%debug)
        self._action_logs(nodename, logfile, "service %s" % svcname, **kwargs)

    def action_node_logs(self, nodename, **kwargs):
        """
        Send node logs.
        kwargs:
        * conn: the connexion socket to the requester
        * backlog: the number of bytes to send from the tail default is 10k.
                   A negative value means send the whole file.
                   The 0 value means follow the file.
        """
        debug = "debug." if kwargs.get("debug") else ""
        logfile = os.path.join(rcEnv.paths.pathlog, "node.%slog"%debug)
        self._action_logs(nodename, logfile, "node", **kwargs)

    def _action_logs(self, nodename, logfile, obj, **kwargs):
        conn = kwargs.get("conn")
        encrypted = kwargs.get("encrypted")
        backlog = kwargs.get("backlog")
        if backlog is None:
            backlog = 1024 * 10
        else:
            backlog = convert_size(backlog, _to='B')
        skip = 0
        if backlog > 0:
            fsize = os.path.getsize(logfile)
            if backlog > fsize:
                skip = 0
            else:
                skip = fsize - backlog

        with open(logfile, "r") as ofile:
            if backlog > 0:
                self.log.info("send %s log to node %s, backlog %d",
                              obj, nodename, backlog)
                try:
                    ofile.seek(skip)
                except Exception as exc:
                    self.log.info(str(exc))
                    ofile.seek(0)
            elif backlog < 0:
                self.log.info("send %s log to node %s, whole file",
                              obj, nodename)
                ofile.seek(0)
            else:
                self.log.info("follow %s log for node %s",
                              obj, nodename)
                ofile.seek(0, 2)
            lines = []
            msg_size = 0
            conn.settimeout(1)
            loops = 0

            if skip:
                # drop first line (that is incomplete as the seek placed the
                # cursor in the middle
                line = ofile.readline()

            while True:
                line = ofile.readline()
                line_size = len(line)
                if line_size == 0:
                    if msg_size > 0:
                        if encrypted:
                            message = self.encrypt(lines)
                        else:
                            message = self.msg_encode(lines)
                        try:
                            conn.sendall(message)
                        except Exception as exc:
                            if hasattr(exc, "errno") and exc.errno == 32:
                                # Broken pipe (client has left)
                                break
                    if backlog != 0:
                        # don't follow file
                        break
                    else:
                        loops += 1
                        # follow
                        if loops > 10:
                            try:
                                conn.send(b"\0")
                                loops = 0
                            except Exception as exc:
                                self.log.info("stop following %s log for node %s: %s",
                                              obj, nodename, exc)
                                break
                        time.sleep(0.1)
                        lines = []
                        msg_size = 0
                        continue
                lines.append(line)
                msg_size += line_size
                if msg_size > shared.MAX_MSG_SIZE:
                    if encrypted:
                        message = self.encrypt(lines)
                    else:
                        message = self.msg_encode(lines)
                    conn.sendall(message)
                    msg_size = 0
                    lines = []

 0707010005c8db000081b40000000000000000000000015ab217540001804c000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/osvcd_mon.py   """
Monitor Thread
"""
import os
import sys
import time
import datetime
import codecs
import glob
import logging
import hashlib
import json
import tempfile
import shutil
import re
import threading
from subprocess import Popen, PIPE
from distutils.version import LooseVersion

import osvcd_shared as shared
import rcExceptions as ex
import json_delta
from comm import Crypt
from rcGlobalEnv import rcEnv, Storage
from rcUtilities import bdecode, purge_cache, fsum
from svcBuilder import build, fix_app_link, fix_exe_link
from freezer import Freezer

STARTED_STATES = [
    "n/a",
    "up",
]
STOPPED_STATES = [
    "n/a",
    "down",
    "stdby up",
    "stdby down",
]

class Monitor(shared.OsvcThread, Crypt):
    """
    The monitoring thread collecting local service states and taking decisions.
    """
    monitor_period = 0.5
    max_shortloops = 30
    default_stdby_nb_restart = 2

    def __init__(self):
        shared.OsvcThread.__init__(self)
        self._shutdown = False
        self.compat = True
        self.last_node_data = None
        self.status_threads = {}

    def run(self):
        self.log = logging.getLogger(rcEnv.nodename+".osvcd.monitor")
        self.set_nmon("init")
        self.log.info("monitor started")
        self.startup = datetime.datetime.utcnow()
        self.rejoin_grace_period_expired = False
        self.shortloops = 0
        self.unfreeze_when_all_nodes_joined = False

        shared.CLUSTER_DATA[rcEnv.nodename] = {
            "compat": shared.COMPAT_VERSION,
            "monitor": shared.NMON_DATA,
            "services": {},
        }

        if os.environ.get("OPENSVC_AGENT_UPGRADE"):
            if not self.freezer.node_frozen():
                self.log.info("freeze node until the cluster is complete")
                self.unfreeze_when_all_nodes_joined = True
                self.freezer.node_freeze()

        # send a first message without service status, so the peers know
        # we are in init state.
        self.update_hb_data()

        try:
            while True:
                self.do()
                if self.stopped():
                    self.join_status_threads()
                    self.join_threads()
                    self.terminate_procs()
                    sys.exit(0)
        except Exception as exc:
            self.log.exception(exc)

    def transition_count(self):
        count = 0
        for svcname, data in shared.SMON_DATA.items():
            if data.status and data.status != "scaling" and data.status.endswith("ing"):
                count += 1
        return count

    def join_status_threads(self):
        """
        Initial service status eval is done is thread.
        Join those still not terminated.
        """
        for thr in self.status_threads.values():
            thr.join()

    def set_next(self, timeout):
        """
        Set the shortloop counter to the value meaning the long loop will
        be run at most in <timeout> seconds.
        """
        target = self.max_shortloops - (timeout // self.monitor_period)
        if target < 0:
            target = 0
        elif target < self.shortloops:
            return
        self.shortloops = target

    def reconfigure(self):
        """
        The node config references may have changed, update the services objects.
        """
        with shared.SERVICES_LOCK:
            for svcname in shared.SERVICES:
                 shared.SERVICES[svcname] = build(svcname, node=shared.NODE)

    def do(self):
        terminated_procs = self.janitor_procs()
        self.janitor_threads()
        if terminated_procs == 0 and not self.mon_changed() and self.shortloops < self.max_shortloops:
            self.shortloops += 1
            if not self.mon_changed():
                with shared.MON_TICKER:
                    shared.MON_TICKER.wait(self.monitor_period)
            return
        if self.mon_changed():
            with shared.MON_TICKER:
                self.log.debug("woken for:")
                for idx, reason in enumerate(shared.MON_CHANGED):
                    self.log.debug("%d. %s", idx, reason)
                self.unset_mon_changed()
        self.shortloops = 0
        self.reload_config()
        self.merge_frozen()
        if self._shutdown:
            if len(self.procs) == 0:
                self.stop()
        else:
            self.update_cluster_data()
            self.merge_hb_data()
            self.orchestrator()
        self.update_hb_data()
        shared.wake_collector()

    def shutdown(self):
        with shared.SERVICES_LOCK:
            for svc in shared.SERVICES.values():
                self.service_shutdown(svc.svcname)
        self._shutdown = True
        shared.wake_monitor("service %s shutdown terminated" % svc.svcname)

    #########################################################################
    #
    # Service config exchange
    #
    #########################################################################
    def sync_services_conf(self):
        """
        For each service, decide if we have an outdated configuration file
        and fetch the most recent one if needed.
        """
        confs = self.get_services_configs()
        for svcname, data in confs.items():
            new_service = False
            with shared.SERVICES_LOCK:
                if svcname not in shared.SERVICES:
                    new_service = True
            instances = self.get_service_instances(svcname, rcEnv.nodename)
            for instance in instances:
                # block config file exchange while any instance is being deleted
                global_expect = instance.get("monitor", {}).get("global_expect")
                if global_expect in ("purged", "deleted"):
                    continue
            if rcEnv.nodename not in data:
                # need to check if we should have this config ?
                new_service = True
            if new_service:
                ref_conf = Storage({
                    "csum": "",
                    "updated": "0",
                })
                ref_nodename = rcEnv.nodename
            else:
                ref_conf = data[rcEnv.nodename]
                ref_nodename = rcEnv.nodename
            for nodename, conf in data.items():
                if rcEnv.nodename == nodename:
                    continue
                if rcEnv.nodename not in conf.get("scope", []):
                    # we are not a service node
                    continue
                if conf.csum != ref_conf.csum and \
                   conf.updated > ref_conf.updated:
                    ref_conf = conf
                    ref_nodename = nodename
            if ref_nodename == rcEnv.nodename:
                # we already have the most recent version
                continue
            with shared.SERVICES_LOCK:
                if svcname in shared.SERVICES and \
                   rcEnv.nodename in shared.SERVICES[svcname].nodes and \
                   ref_nodename in shared.SERVICES[svcname].drpnodes:
                    # don't fetch drp config from prd nodes
                    return
            self.log.info("node %s has the most recent service %s config",
                          ref_nodename, svcname)
            if new_service:
                Freezer(svcname).freeze()
            self.fetch_service_config(svcname, ref_nodename)
            if new_service:
                fix_exe_link(svcname)
                fix_app_link(svcname)

    def fetch_service_config(self, svcname, nodename):
        """
        Fetch and install the most recent service configuration file, using
        the remote node listener.
        """
        request = {
            "action": "get_service_config",
            "options": {
                "svcname": svcname,
            },
        }
        resp = self.daemon_send(request, nodename=nodename)
        status = resp.get("status", 1)
        if status == 2:
            # peer is deleting this service
            self.log.info(resp.get("error", ""))
            return
        elif status != 0:
            self.log.error("unable to fetch service %s config from node %s: "
                           "received %s", svcname, nodename, resp)
            return
        with tempfile.NamedTemporaryFile(dir=rcEnv.paths.pathtmp, delete=False) as filep:
            tmpfpath = filep.name
        try:
            with codecs.open(tmpfpath, "w", "utf-8") as filep:
                filep.write(resp["data"])
            with shared.SERVICES_LOCK:
                if svcname in shared.SERVICES:
                    svc = shared.SERVICES[svcname]
                else:
                    svc = None
            if svc:
                try:
                    results = svc._validate_config(path=filep.name)
                except Exception as exc:
                    self.log.error("service %s fetched config validation "
                                   "error: %s", svcname, exc)
                    return
            else:
                results = {"errors": 0}
            if results["errors"] == 0:
                dst = os.path.join(rcEnv.paths.pathetc, svcname+".conf")
                shutil.copy(filep.name, dst)
                mtime = resp.get("mtime")
                if mtime:
                    os.utime(dst, (mtime, mtime))
            else:
                self.log.error("the service %s config fetched from node %s is "
                               "not valid", svcname, nodename)
                return
        finally:
            os.unlink(tmpfpath)
        shared.SERVICES[svcname] = build(svcname, node=shared.NODE)
        try:
            shared.SERVICES[svcname].purge_status_data_dump()
            shared.SERVICES[svcname].print_status_data_eval()
        except Exception:
            # can happen when deleting the service
            pass

        self.log.info("the service %s config fetched from node %s is now "
                      "installed", svcname, nodename)

    #########################################################################
    #
    # Node and Service Commands
    #
    #########################################################################
    def generic_callback(self, svcname, **kwargs):
        self.set_smon(svcname, **kwargs)
        self.update_hb_data()

    def node_stonith(self, node):
        proc = self.node_command(["stonith", "--node", node])
        self.push_proc(proc=proc)

    def service_start_resources(self, svcname, rids):
        self.set_smon(svcname, "restarting")
        proc = self.service_command(svcname, ["start", "--rid", ",".join(rids)])
        self.push_proc(
            proc=proc,
            on_success="service_start_resources_on_success",
            on_success_args=[svcname, rids],
            on_error="generic_callback",
            on_error_args=[svcname],
            on_error_kwargs={"status": "idle"},
        )

    def service_start_resources_on_success(self, svcname, rids):
        self.set_smon(svcname, status="idle")
        for rid in rids:
            self.reset_smon_retries(svcname, rid)
        self.update_hb_data()

    def service_toc(self, svcname):
        proc = self.service_command(svcname, ["toc"])
        self.push_proc(
            proc=proc,
            on_success="generic_callback",
            on_success_args=[svcname],
            on_success_kwargs={"status": "idle"},
            on_error="generic_callback",
            on_error_args=[svcname],
            on_error_kwargs={"status": "toc failed"},
        )

    def service_start(self, svcname):
        self.set_smon(svcname, "starting")
        proc = self.service_command(svcname, ["start"])
        self.push_proc(
            proc=proc,
            on_success="generic_callback",
            on_success_args=[svcname],
            on_success_kwargs={"status": "idle", "local_expect": "started"},
            on_error="generic_callback",
            on_error_args=[svcname],
            on_error_kwargs={"status": "start failed"},
        )

    def service_stop(self, svcname):
        self.set_smon(svcname, "stopping")
        proc = self.service_command(svcname, ["stop"])
        self.push_proc(
            proc=proc,
            on_success="generic_callback",
            on_success_args=[svcname],
            on_success_kwargs={"status": "idle", "local_expect": "unset"},
            on_error="generic_callback",
            on_error_args=[svcname],
            on_error_kwargs={"status": "stop failed"},
        )

    def service_shutdown(self, svcname):
        self.set_smon(svcname, "shutdown")
        proc = self.service_command(svcname, ["shutdown"])
        self.push_proc(
            proc=proc,
            on_success="generic_callback",
            on_success_args=[svcname],
            on_success_kwargs={"status": "idle", "local_expect": "unset"},
            on_error="generic_callback",
            on_error_args=[svcname],
            on_error_kwargs={"status": "shutdown failed"},
        )

    def service_delete(self, svcname):
        self.set_smon(svcname, "deleting", local_expect="unset")
        proc = self.service_command(svcname, ["delete"])
        self.push_proc(
            proc=proc,
            on_success="generic_callback",
            on_success_args=[svcname],
            on_success_kwargs={"status": "idle"},
            on_error="generic_callback",
            on_error_args=[svcname],
            on_error_kwargs={"status": "delete failed"},
        )

    def service_purge(self, svcname):
        self.set_smon(svcname, "unprovisioning")
        proc = self.service_command(svcname, ["unprovision"])
        self.push_proc(
            proc=proc,
            on_success="service_purge_on_success",
            on_success_args=[svcname],
            on_error="generic_callback",
            on_error_args=[svcname],
            on_error_kwargs={"status": "purge failed"},
        )

    def service_purge_on_success(self, svcname):
        self.set_smon(svcname, "deleting", local_expect="unset")
        proc = self.service_command(svcname, ["delete"])
        self.push_proc(
            proc=proc,
            on_success="generic_callback",
            on_success_args=[svcname],
            on_success_kwargs={"status": "idle"},
            on_error="generic_callback",
            on_error_args=[svcname],
            on_error_kwargs={"status": "purge failed"},
        )

    def service_provision(self, svc):
        self.set_smon(svc.svcname, "provisioning")
        candidates = self.placement_candidates(svc, discard_frozen=False,
                                               discard_overloaded=False,
                                               discard_unprovisioned=False,
                                               discard_constraints_violation=False)
        cmd = ["provision"]
        if self.placement_leader(svc, candidates):
            cmd += ["--disable-rollback"]
        proc = self.service_command(svc.svcname, cmd)
        self.push_proc(
            proc=proc,
            on_success="service_thaw",
            on_success_args=[svc.svcname],
            on_error="generic_callback",
            on_error_args=[svc.svcname],
            on_error_kwargs={"status": "provision failed"},
        )

    def service_unprovision(self, svcname):
        self.set_smon(svcname, "unprovisioning", local_expect="unset")
        proc = self.service_command(svcname, ["unprovision"])
        self.push_proc(
            proc=proc,
            on_success="generic_callback",
            on_success_args=[svcname],
            on_success_kwargs={"status": "idle", "local_expect": "unset"},
            on_error="generic_callback",
            on_error_args=[svcname],
            on_error_kwargs={"status": "unprovision failed"},
        )

    def wait_global_expect_change(self, svcname, ref, timeout):
        for step in range(timeout):
            global_expect = shared.SMON_DATA.get(svcname, {}).get("global_expect")
            if global_expect != ref:
                return True
            time.sleep(1)
        return False

    def service_set_flex_instances(self, svcname, instances):
        cmd = [
            "set",
            "--kw", "flex_min_nodes=%d" % instances,
            "--kw", "flex_max_nodes=%d" % instances,
        ]
        proc = self.service_command(svcname, cmd)
        out, err = proc.communicate()
        return proc.returncode

    def service_create_scaler_slave(self, svcname, svc, data, instances=None):
        data["DEFAULT"]["scaler_slave"] = "true"
        if svc.topology == "flex" and instances is not None:
            data["DEFAULT"]["flex_min_nodes"] = instances
            data["DEFAULT"]["flex_max_nodes"] = instances
        try:
            del data["DEFAULT"]["scale"]
        except KeyError:
            pass
        cmd = ["create"]
        proc = self.service_command(svcname, cmd, stdin=json.dumps(data))
        out, err = proc.communicate()
        if proc.returncode != 0:
            self.set_smon(svcname, "create failed")

        try:
            ret = self.wait_service_config_consensus(svcname, svc.peers)
        except Exception as exc:
            self.log.exception(exc)
            return

        self.set_smon(svcname, global_expect="thawed")
        if not self.wait_global_expect_change(svcname, "thawed", 600):
            self.set_smon(svcname, "thaw failed", global_expect="unset")
            return
        self.set_smon(svcname, global_expect="provisioned")
        self.wait_global_expect_change(svcname, "provisioned", 600)

    def service_freeze(self, svcname):
        self.set_smon(svcname, "freezing")
        proc = self.service_command(svcname, ["freeze"])
        self.push_proc(
            proc=proc,
            on_success="generic_callback",
            on_success_args=[svcname],
            on_success_kwargs={"status": "idle"},
            on_error="generic_callback",
            on_error_args=[svcname],
            on_error_kwargs={"status": "idle"},
        )

    def service_thaw(self, svcname):
        self.set_smon(svcname, "thawing")
        proc = self.service_command(svcname, ["thaw"])
        self.push_proc(
            proc=proc,
            on_success="generic_callback",
            on_success_args=[svcname],
            on_success_kwargs={"status": "idle"},
            on_error="generic_callback",
            on_error_args=[svcname],
            on_error_kwargs={"status": "idle"},
        )


    #########################################################################
    #
    # Orchestration
    #
    #########################################################################
    def orchestrator(self):
        if shared.NMON_DATA.status == "init":
            return

        # node
        self.node_orchestrator()

        # services (iterate over deleting services too)
        svcnames = [svcname for svcname in shared.SMON_DATA]
        self.get_agg_services()
        for svcname in svcnames:
            transitions = self.transition_count()
            if transitions > shared.NODE.max_parallel:
                self.duplog("info", "delay services orchestration: "
                            "%(transitions)d/%(max)d transitions already "
                            "in progress", transitions=transitions,
                            max=shared.NODE.max_parallel)
                break
            if self.status_older_than_cf(svcname):
                #self.log.info("%s status dump is older than its config file",
                #              svcname)
                continue
            svc = self.get_service(svcname)
            self.service_orchestrator(svcname, svc)
            self.resources_orchestrator(svcname, svc)
        self.sync_services_conf()

    def resources_orchestrator(self, svcname, svc):
        if svc is None:
            return
        if self.service_frozen(svcname) or self.freezer.node_frozen():
            #self.log.info("resource %s orchestrator out (frozen)", svc.svcname)
            return
        if svc.disabled:
            #self.log.info("resource %s orchestrator out (disabled)", svc.svcname)
            return
        try:
            with shared.CLUSTER_DATA_LOCK:
                resources = shared.CLUSTER_DATA[rcEnv.nodename]["services"]["status"][svc.svcname]["resources"]
        except KeyError:
            return

        def monitored_resource(svc, rid, resource):
            if not resource.get("monitor"):
                return False
            if resource.get("disable"):
                return False
            if smon.local_expect != "started":
                return False
            nb_restart = svc.get_resource(rid).nb_restart
            if nb_restart == 0 and resource.get("standby"):
                nb_restart = self.default_stdby_nb_restart
            retries = self.get_smon_retries(svc.svcname, rid)
            if retries > nb_restart:
                return False
            if retries >= nb_restart:
                self.inc_smon_retries(svc.svcname, rid)
                self.log.info("max retries (%d) reached for resource %s.%s",
                              nb_restart, svc.svcname, rid)
                self.service_toc(svc.svcname)
                return False
            self.inc_smon_retries(svc.svcname, rid)
            self.log.info("restart resource %s.%s, try %d/%d", svc.svcname, rid,
                          retries+1, nb_restart)
            return True

        def stdby_resource(svc, rid, resource):
            if resource.get("standby") is not True:
                return False
            nb_restart = svc.get_resource(rid).nb_restart
            if nb_restart < self.default_stdby_nb_restart:
                nb_restart = self.default_stdby_nb_restart
            retries = self.get_smon_retries(svc.svcname, rid)
            if retries > nb_restart:
                return False
            if retries >= nb_restart:
                self.inc_smon_retries(svc.svcname, rid)
                self.log.info("max retries (%d) reached for standby resource "
                              "%s.%s", nb_restart, svc.svcname, rid)
                return False
            self.inc_smon_retries(svc.svcname, rid)
            self.log.info("start standby resource %s.%s, try %d/%d", svc.svcname, rid,
                          retries+1, nb_restart)
            return True

        smon = self.get_service_monitor(svc.svcname)
        if smon.status != "idle":
            return

        rids = []
        for rid, resource in resources.items():
            if resource["status"] not in ("warn", "down", "stdby down"):
                continue
            if resource.get("provisioned", {}).get("state") is False:
                continue
            if monitored_resource(svc, rid, resource) or stdby_resource(svc, rid, resource):
                rids.append(rid)
                continue

        if len(rids) > 0:
            self.service_start_resources(svc.svcname, rids)

    def node_orchestrator(self):
        self.orchestrator_auto_grace()
        nmon = self.get_node_monitor()
        node_frozen = self.freezer.node_frozen()
        if self.unfreeze_when_all_nodes_joined and node_frozen and len(self.cluster_nodes) == len(shared.CLUSTER_DATA):
            self.log.info("thaw node now the cluster is complete")
            self.freezer.node_thaw()
            self.unfreeze_when_all_nodes_joined = False
            node_frozen = False
        if nmon.status != "idle":
            return
        self.set_nmon_g_expect_from_status()
        if nmon.global_expect == "frozen":
            self.unfreeze_when_all_nodes_joined = False
            if not node_frozen:
                self.log.info("freeze node")
                self.freezer.node_freeze()
        elif nmon.global_expect == "thawed":
            self.unfreeze_when_all_nodes_joined = False
            if node_frozen:
                self.log.info("thaw node")
                self.freezer.node_thaw()

    def service_orchestrator(self, svcname, svc):
        smon = self.get_service_monitor(svcname)
        if svc is None:
            if smon and svcname in shared.AGG:
                # deleting service: unset global expect if done cluster-wide
                status = shared.AGG[svcname].avail
                self.set_smon_g_expect_from_status(svcname, smon, status)
            return
        if smon.global_expect != "aborted" and \
           smon.status not in ("ready", "idle", "wait children", "wait parents"):
            #self.log.info("service %s orchestrator out (mon status %s)", svc.svcname, smon.status)
            return
        status = shared.AGG[svc.svcname].avail
        self.set_smon_g_expect_from_status(svc.svcname, smon, status)
        if smon.global_expect:
            self.service_orchestrator_manual(svc, smon, status)
        else:
            self.service_orchestrator_auto(svc, smon, status)

    @staticmethod
    def scale_svcname(svcname, idx):
        return str(idx)+"."+svcname

    def service_orchestrator_auto(self, svc, smon, status):
        """
        Automatic instance start decision.
        Verifies hard and soft affinity and anti-affinity, then routes to
        failover and flex specific policies.
        """
        if svc.disabled:
            #self.log.info("service %s orchestrator out (disabled)", svc.svcname)
            return
        if not self.compat:
            return
        if svc.topology == "failover" and smon.local_expect == "started":
            return
        if self.service_frozen(svc.svcname) or self.freezer.node_frozen():
            #self.log.info("service %s orchestrator out (frozen)", svc.svcname)
            return
        if not self.rejoin_grace_period_expired:
            return
        if svc.scale_target is not None and smon.global_expect is None:
            self.service_orchestrator_scaler(svc)
            return
        if status in (None, "undef", "n/a"):
            #self.log.info("service %s orchestrator out (agg avail status %s)",
            #              svc.svcname, status)
            return
        if svc.hard_anti_affinity:
            intersection = set(self.get_local_svcnames()) & set(svc.hard_anti_affinity)
            if len(intersection) > 0:
                #self.log.info("service %s orchestrator out (hard anti-affinity with %s)",
                #              svc.svcname, ','.join(intersection))
                return
        if svc.hard_affinity:
            intersection = set(self.get_local_svcnames()) & set(svc.hard_affinity)
            if len(intersection) < len(set(svc.hard_affinity)):
                #self.log.info("service %s orchestrator out (hard affinity with %s)",
                #              svc.svcname, ','.join(intersection))
                return

        candidates = self.placement_candidates(svc)
        if candidates != [rcEnv.nodename]:
            # the local node is not the only candidate, we can apply soft
            # affinity filtering
            if svc.soft_anti_affinity:
                intersection = set(self.get_local_svcnames()) & set(svc.soft_anti_affinity)
                if len(intersection) > 0:
                    #self.log.info("service %s orchestrator out (soft anti-affinity with %s)",
                    #              svc.svcname, ','.join(intersection))
                    return
            if svc.soft_affinity:
                intersection = set(self.get_local_svcnames()) & set(svc.soft_affinity)
                if len(intersection) < len(set(svc.soft_affinity)):
                    #self.log.info("service %s orchestrator out (soft affinity with %s)",
                    #              svc.svcname, ','.join(intersection))
                    return

        if svc.topology == "failover":
            self.service_orchestrator_auto_failover(svc, smon, status, candidates)
        elif svc.topology == "flex":
            self.service_orchestrator_auto_flex(svc, smon, status, candidates)

    def service_orchestrator_auto_failover(self, svc, smon, status, candidates):
        if svc.orchestrate == "start":
            ranks = self.placement_ranks(svc, candidates=svc.peers)
            if ranks == []:
                return
            nodename = ranks[0]
            if nodename == rcEnv.nodename:
                # natural leader
                pass
            else:
                return
        instance = self.get_service_instance(svc.svcname, rcEnv.nodename)
        if smon.status in ("ready", "wait parents"):
            if instance.avail is "up":
                self.log.info("abort 'ready' because the local instance "
                              "has started")
                self.set_smon(svc.svcname, "idle")
                return
            if status == "up":
                self.log.info("abort 'ready' because an instance has started")
                self.set_smon(svc.svcname, "idle")
                return
            peer = self.better_peer_ready(svc, candidates)
            if peer:
                self.log.info("abort 'ready' because node %s has a better "
                              "placement score for service %s and is also "
                              "ready", peer, svc.svcname)
                self.set_smon(svc.svcname, "idle")
                return
            peer = self.peer_transitioning(svc.svcname)
            if peer:
                self.log.info("abort 'ready' because node %s is already "
                              "acting on service %s", peer, svc.svcname)
                self.set_smon(svc.svcname, "idle")
                return
        if smon.status == "wait parents":
            if self.parents_available(svc):
                self.set_smon(svc.svcname, status="idle")
                return
        elif smon.status == "ready":
            now = time.time()
            if smon.status_updated < (now - self.ready_period):
                self.log.info("failover service %s status %s/ready for "
                              "%d seconds", svc.svcname, status,
                              now-smon.status_updated)
                if smon.stonith and smon.stonith not in shared.CLUSTER_DATA:
                    # stale peer which previously ran the service
                    self.node_stonith(smon.stonith)
                self.service_start(svc.svcname)
                return
            tmo = int(smon.status_updated + self.ready_period - now) + 1
            self.log.info("service %s will start in %d seconds",
                          svc.svcname, tmo)
            self.set_next(tmo)
        elif smon.status == "idle":
            if svc.orchestrate == "no" and smon.global_expect != "started":
                return
            if status not in ("down", "stdby down", "stdby up"):
                return
            if not self.parents_available(svc):
                self.set_smon(svc.svcname, status="wait parents")
                return
            if len(svc.peers) == 1:
                self.log.info("failover service %s status %s/idle and "
                              "single node", svc.svcname, status)
                self.service_start(svc.svcname)
                return
            if not self.placement_leader(svc, candidates):
                return
            self.log.info("failover service %s status %s", svc.svcname,
                          status)
            self.set_smon(svc.svcname, "ready")

    def service_orchestrator_auto_flex(self, svc, smon, status, candidates):
        if svc.orchestrate == "start":
            ranks = self.placement_ranks(svc, candidates=svc.peers)
            if ranks == []:
                return
            try:
                idx = ranks.index(rcEnv.nodename)
            except ValueError:
                return
            if rcEnv.nodename not in ranks[:svc.flex_min_nodes]:
                # not a natural leader, skip orchestration
                return
        instance = self.get_service_instance(svc.svcname, rcEnv.nodename)
        up_nodes = self.up_service_instances(svc.svcname)
        n_up = len(up_nodes)
        n_missing = svc.flex_min_nodes - n_up

        if smon.status in ("ready", "wait parents"):
            if n_up > svc.flex_min_nodes:
                self.log.info("flex service %s instance count reached "
                              "required minimum while we were ready",
                              svc.svcname)
                self.set_smon(svc.svcname, "idle")
                return
            better_peers = self.better_peers_ready(svc);
            if n_missing > 0 and len(better_peers) >= n_missing:
                self.log.info("abort 'ready' because nodes %s have a better "
                              "placement score for service %s and are also "
                              "ready", ','.join(better_peers), svc.svcname)
                self.set_smon(svc.svcname, "idle")
                return
        if smon.status == "wait parents":
            if self.parents_available(svc):
                self.set_smon(svc.svcname, status="idle")
                return
        if smon.status == "ready":
            now = time.time()
            if smon.status_updated < (now - self.ready_period):
                self.log.info("flex service %s status %s/ready for %d seconds",
                              svc.svcname, status, now-smon.status_updated)
                self.service_start(svc.svcname)
            else:
                tmo = int(smon.status_updated + self.ready_period - now) + 1
                self.log.info("service %s will start in %d seconds",
                              svc.svcname, tmo)
                self.set_next(tmo)
        elif smon.status == "idle":
            if svc.orchestrate == "no" and smon.global_expect != "started":
                return
            if n_up < svc.flex_min_nodes:
                if instance.avail not in STOPPED_STATES:
                    return
                if not self.placement_leader(svc, candidates):
                    return
                if not self.parents_available(svc):
                    self.set_smon(svc.svcname, status="wait parents")
                    return
                self.log.info("flex service %s started, starting or ready to "
                              "start instances: %d/%d-%d. local status %s",
                              svc.svcname, n_up, svc.flex_min_nodes,
                              svc.flex_max_nodes, instance.avail)
                self.set_smon(svc.svcname, "ready")
            elif n_up > svc.flex_max_nodes:
                if instance is None:
                    return
                if instance.avail not in STARTED_STATES:
                    return
                n_to_stop = n_up - svc.flex_max_nodes
                overloaded_up_nodes = self.overloaded_up_service_instances(svc.svcname)
                to_stop = self.placement_ranks(svc, candidates=overloaded_up_nodes)[-n_to_stop:]
                n_to_stop -= len(to_stop)
                if n_to_stop > 0:
                    to_stop += self.placement_ranks(svc, candidates=set(up_nodes)-set(overloaded_up_nodes))[-n_to_stop:]
                self.log.info("%d nodes to stop to honor service %s "
                              "flex_max_nodes=%d. choose %s",
                              n_to_stop, svc.svcname, svc.flex_max_nodes,
                              ", ".join(to_stop))
                if rcEnv.nodename not in to_stop:
                    return
                self.log.info("flex service %s started, starting or ready to "
                              "start instances: %d/%d-%d. local status %s",
                              svc.svcname, n_up, svc.flex_min_nodes,
                              svc.flex_max_nodes, instance.avail)
                self.service_stop(svc.svcname)

    def service_orchestrator_manual(self, svc, smon, status):
        """
        Take actions to meet global expect target, set by user or by
        service_orchestrator_auto()
        """
        instance = self.get_service_instance(svc.svcname, rcEnv.nodename)
        if smon.global_expect == "frozen":
            if self.service_frozen(svc.svcname) is False:
                self.log.info("freeze service %s", svc.svcname)
                self.service_freeze(svc.svcname)
        elif smon.global_expect == "thawed":
            if self.service_frozen(svc.svcname):
                self.log.info("thaw service %s", svc.svcname)
                self.service_thaw(svc.svcname)
        elif smon.global_expect == "shutdown":
            if not self.children_down(svc):
                self.set_smon(svc.svcname, status="wait children")
                return
            elif smon.status == "wait children":
                self.set_smon(svc.svcname, status="idle")

            if not self.service_frozen(svc.svcname):
                self.log.info("freeze service %s", svc.svcname)
                self.service_freeze(svc.svcname)
            elif not self.is_instance_shutdown(instance):
                thawed_on = self.service_instances_thawed(svc.svcname)
                if thawed_on:
                    self.duplog("info", "service %(svcname)s still has thawed instances "
                                "on nodes %(thawed_on)s, delay shutdown",
                                svcname=svc.svcname,
                                thawed_on=", ".join(thawed_on))
                else:
                    self.service_shutdown(svc.svcname)
        elif smon.global_expect == "stopped":
            if not self.children_down(svc):
                self.set_smon(svc.svcname, status="wait children")
                return
            elif smon.status == "wait children":
                self.set_smon(svc.svcname, status="idle")

            if not self.service_frozen(svc.svcname):
                self.log.info("freeze service %s", svc.svcname)
                self.service_freeze(svc.svcname)
            elif instance.avail not in STOPPED_STATES:
                thawed_on = self.service_instances_thawed(svc.svcname)
                if thawed_on:
                    self.duplog("info", "service %(svcname)s still has thawed instances "
                                "on nodes %(thawed_on)s, delay stop",
                                svcname=svc.svcname,
                                thawed_on=", ".join(thawed_on))
                else:
                    self.service_stop(svc.svcname)
        elif smon.global_expect == "started":
            if self.service_frozen(svc.svcname):
                self.log.info("thaw service %s", svc.svcname)
                self.service_thaw(svc.svcname)
            elif status not in STARTED_STATES:
                self.service_orchestrator_auto(svc, smon, status)
        elif smon.global_expect == "unprovisioned":
            if not self.service_unprovisioned(instance) and \
               self.leader_first(svc, provisioned=False):
                self.service_unprovision(svc.svcname)
        elif smon.global_expect == "provisioned":
            if not self.service_provisioned(instance) and \
               self.leader_first(svc, provisioned=True):
                self.service_provision(svc)
        elif smon.global_expect == "deleted":
            if svc.svcname in shared.SERVICES:
                self.service_delete(svc.svcname)
        elif smon.global_expect == "purged" and \
             self.leader_first(svc, provisioned=False, deleted=True, check_min_instances_reached=False):
            if svc.svcname in shared.SERVICES and \
               (not self.service_unprovisioned(instance) or instance is not None):
                self.service_purge(svc.svcname)
        elif smon.global_expect == "aborted" and \
             smon.local_expect not in (None, "started"):
            self.set_smon(svc.svcname, local_expect="unset")
        elif smon.global_expect == "placed":
            if instance["monitor"].get("placement") != "leader":
                if instance.avail not in STOPPED_STATES:
                    self.service_stop(svc.svcname)
            elif self.non_leaders_stopped(svc) and \
                 (shared.AGG[svc.svcname].placement not in ("optimal", "n/a") or shared.AGG[svc.svcname].avail != "up") and \
                 instance.avail in STOPPED_STATES:
                self.service_start(svc.svcname)

    def service_orchestrator_scaler(self, svc):
        smon = self.get_service_monitor(svc.svcname)
        if smon.status != "idle":
            return
        peer = self.peer_transitioning(svc.svcname)
        if peer:
            return
        candidates = self.placement_candidates(
            svc, discard_frozen=False,
            discard_overloaded=False,
            discard_unprovisioned=False,
            discard_constraints_violation=False,
            discard_start_failed=False
        )
        if self.placement_ranks(svc, candidates)[0] != rcEnv.nodename:
            # not natural leader
            return
        current_slaves = [svcname for svcname in shared.SERVICES \
                          if re.match("^[0-9]+\."+svc.svcname+"$", svcname)]
        n_slots = self.scaler_slots(current_slaves)
        if n_slots == svc.scale_target:
            return
        missing = svc.scale_target - n_slots
        self.log.info("service %s scale delta %d on target %d", svc.svcname,
                      missing, svc.scale_target)
        if missing > 0:
            self.service_orchestrator_scaler_up(svc, missing, current_slaves)
        else:
            self.service_orchestrator_scaler_down(svc, missing, current_slaves)

    def service_orchestrator_scaler_up(self, svc, missing, current_slaves):
        if svc.topology == "flex":
            self.service_orchestrator_scaler_up_flex(svc, missing, current_slaves)
        else:
            self.service_orchestrator_scaler_up_failover(svc, missing, current_slaves)

    def service_orchestrator_scaler_down(self, svc, missing, current_slaves):
        if svc.topology == "flex":
            self.service_orchestrator_scaler_down_flex(svc, missing, current_slaves)
        else:
            self.service_orchestrator_scaler_down_failover(svc, missing, current_slaves)

    def service_orchestrator_scaler_up_flex(self, svc, missing, current_slaves):
        candidates = self.placement_candidates(svc, discard_preserved=False)
        width = len(candidates)
        if width == 0:
            return

        # start fill-up the current slaves that might have holes due to
        # previous scaling while some nodes where overloaded
        n_current_slaves = len(current_slaves)
        current_slaves = sorted(current_slaves, key=LooseVersion)
        for slavename in current_slaves:
            slave = shared.SERVICES[slavename]
            if slave.flex_max_nodes >= width:
                continue
            remain = width - slave.flex_max_nodes
            if remain > missing:
                pad = remain - missing
                new_width = slave.flex_max_nodes + pad
            else:
                pad = remain
                new_width = width
            ret = self.service_set_flex_instances(slavename, new_width)
            if ret != 0:
                self.set_smon(slavename, "set failed")
            else:
                missing -= pad

        left = missing % width
        slaves_count = missing // width
        if left:
            slaves_count += 1

        if slaves_count == 0:
            return

        to_add = []
        max_burst = 3

        # create services in holes first
        for slavename in [str(idx)+"."+svc.svcname for idx in range(n_current_slaves)]:
            if slavename in current_slaves:
                continue
            to_add.append([slavename, width])
            slaves_count -= 1
            if slaves_count == 0:
                break

        to_add += [[str(n_current_slaves+idx)+"."+svc.svcname, width] for idx in range(slaves_count)]
        if left != 0 and len(to_add):
            to_add[-1][1] = left
        to_add = to_add[:max_burst]
        delta = "add " + ",".join([elem[0] for elem in to_add])
        self.log.info("scale service %s: %s", svc.svcname, delta)
        self.set_smon(svc.svcname, status="scaling")
        thr = threading.Thread(target=self.scaling_worker, args=(svc, to_add, []))
        thr.start()
        self.threads.append(thr)

    def service_orchestrator_scaler_down_flex(self, svc, missing, current_slaves):
        to_remove = []
        excess = -missing
        for slavename in sorted(current_slaves, key=LooseVersion, reverse=True):
            slave = shared.SERVICES[slavename]
            n_slots = slave.flex_min_nodes
            if n_slots > excess:
                width = n_slots - excess
                ret = self.service_set_flex_instances(slavename, width)
                if ret != 0:
                    self.set_smon(slavename, "set failed")
                break
            else:
                to_remove.append(slavename)
                excess -= n_slots
        if len(to_remove) == 0:
            return
        delta = "delete " + ",".join(to_remove)
        self.log.info("scale service %s: %s", svc.svcname, delta)
        self.set_smon(svc.svcname, status="scaling")
        thr = threading.Thread(target=self.scaling_worker, args=(svc, [], to_remove))
        thr.start()
        self.threads.append(thr)

    def service_orchestrator_scaler_up_failover(self, svc, missing, current_slaves):
        slaves_count = missing
        n_current_slaves = len(current_slaves)
        new_slaves_list = [str(n_current_slaves+idx)+"."+svc.svcname for idx in range(slaves_count)]

        to_add = sorted(new_slaves_list, key=LooseVersion)
        to_add = [[svcname, None] for svcname in to_add]
        delta = "add " + ",".join([elem[0] for elem in to_add])
        self.log.info("scale service %s: %s", svc.svcname, delta)
        self.set_smon(svc.svcname, status="scaling")
        thr = threading.Thread(target=self.scaling_worker, args=(svc, to_add, []))
        thr.start()
        self.threads.append(thr)

    def service_orchestrator_scaler_down_failover(self, svc, missing, current_slaves):
        slaves_count = -missing
        n_current_slaves = len(current_slaves)
        slaves_list = [str(n_current_slaves-1-idx)+"."+svc.svcname for idx in range(slaves_count)]

        to_remove = sorted(slaves_list, key=LooseVersion)
        to_remove = [svcname for svcname in to_remove]
        delta = "delete " + ",".join([elem[0] for elem in to_remove])
        self.log.info("scale service %s: %s", svc.svcname, delta)
        self.set_smon(svc.svcname, status="scaling")
        thr = threading.Thread(target=self.scaling_worker, args=(svc, [], to_remove))
        thr.start()
        self.threads.append(thr)

    def scaling_worker(self, svc, to_add, to_remove):
        threads = []
        for svcname, instances in to_add:
            if svcname in shared.SERVICES:
                continue
            data = svc.print_config_data()
            thr = threading.Thread(
                target=self.service_create_scaler_slave,
                args=(svcname, svc, data, instances)
            )
            thr.start()
            threads.append(thr)
        for svcname in to_remove:
            if svcname not in shared.SERVICES:
                continue
            self.set_smon(svcname, global_expect="purged")
        for svcname in to_remove:
            self.wait_global_expect_change(svcname, "purged", 300)
        while True:
            for thr in threads:
                thr.join(0)
            if any(thr.is_alive() for thr in threads):
                time.sleep(1)
                if self.stopped():
                    break
                continue
            break
        self.set_smon(svc.svcname, global_expect="unset", status="idle")

    def orchestrator_auto_grace(self):
        """
        After daemon startup, wait for <rejoin_grace_period_expired> seconds
        before allowing service_orchestrator_auto() to proceed.
        """
        if self.rejoin_grace_period_expired:
            return False
        if len(self.cluster_nodes) == 1:
            self.rejoin_grace_period_expired = True
            self.duplog("info", "end of rejoin grace period: single node cluster",
                        nodename="")
            return False
        n_idle = len([1 for node in shared.CLUSTER_DATA.values() if node.get("monitor", {}).get("status") == "idle"])
        if n_idle >= len(self.cluster_nodes):
            self.rejoin_grace_period_expired = True
            self.duplog("info", "end of rejoin grace period: now rejoined",
                        nodename="")
            return False
        now = datetime.datetime.utcnow()
        if now > self.startup + datetime.timedelta(seconds=self.rejoin_grace_period):
            self.rejoin_grace_period_expired = True
            self.freezer.node_freeze()
            self.log.info("rejoin grace period expired, but some nodes "
                        "are still unreacheable. freeze node.")
            return False
        self.duplog("info", "in rejoin grace period", nodename="")
        return True

    def children_down(self, svc):
        missing = []
        if len(svc.children_and_slaves) == 0:
            return True
        for child in svc.children_and_slaves:
            if child == svc.svcname:
                continue
            try:
                avail = shared.AGG[child].avail
            except KeyError:
                avail = "unknown"
            if avail in STOPPED_STATES + ["unknown"]:
                continue
            missing.append(child)
        if len(missing) == 0:
            self.duplog("info", "service %(svcname)s children all avail down",
                        svcname=svc.svcname)
            return True
        self.duplog("info", "service %(svcname)s children still available:"
                    " %(missing)s", svcname=svc.svcname,
                    missing=" ".join(missing))
        return False

    def parents_available(self, svc):
        missing = []
        if len(svc.parents) == 0:
            return True
        for parent in svc.parents:
            if parent == svc.svcname:
                continue
            try:
                avail = shared.AGG[parent].avail
            except KeyError:
                avail = "unknown"
            if avail in STARTED_STATES + ["unknown"]:
                continue
            missing.append(parent)
        if len(missing) == 0:
            self.duplog("info", "service %(svcname)s parents all avail up",
                        svcname=svc.svcname)
            return True
        self.duplog("info", "service %(svcname)s parents not available:"
                    " %(missing)s", svcname=svc.svcname,
                    missing=" ".join(missing))
        return False

    def min_instances_reached(self, svc):
        instances = self.get_service_instances(svc.svcname, discard_empty=False)
        live_nodes = [nodename for nodename in shared.CLUSTER_DATA if shared.CLUSTER_DATA[nodename] is not None]
        min_instances = set(svc.peers) & set(live_nodes)
        return len(instances) >= len(min_instances)

    def non_leaders_stopped(self, svc):
        for nodename, instance in self.get_service_instances(svc.svcname).items():
            if instance["monitor"].get("placement") == "leader":
                continue
            if instance.get("avail") not in STOPPED_STATES:
                self.log.info("service '%s' instance node '%s' is not stopped yet",
                              svc.svcname, nodename)
                return False
        return True

    def leader_first(self, svc, provisioned=False, deleted=None, check_min_instances_reached=True):
        """
        Return True if the peer selected for anteriority is found to have
        reached the target status, or if the local node is the one with
        anteriority.

        Anteriority selection is done with these criteria:

        * choose the placement top node amongst node with a up instance
        * if none, choose the placement top node amongst all nodes,
          whatever their frozen, and current provisioning state. Still
          honor the constraints and overload discards.
        """
        if check_min_instances_reached and not self.min_instances_reached(svc):
            self.log.info("delay leader-first action on service %s until all "
                          "nodes have fetched the service config", svc.svcname)
            return False
        instances = self.get_service_instances(svc.svcname, discard_empty=True)
        candidates = [nodename for (nodename, data) in instances.items() \
                      if data.get("avail") in ("up", "warn")]
        if len(candidates) == 0:
            self.log.info("service %s has no up instance, relax candidates "
                          "constraints", svc.svcname)
            candidates = self.placement_candidates(
                svc, discard_frozen=False,
                discard_unprovisioned=False,
            )
        try:
            top = self.placement_ranks(svc, candidates=candidates)[0]
            self.log.info("elected %s as the first node to take action on "
                          "service %s", top, svc.svcname)
        except IndexError:
            self.log.error("service %s placement ranks list is empty", svc.svcname)
            return True
        if top == rcEnv.nodename:
            return True
        instance = self.get_service_instance(svc.svcname, top)
        if instance is None and deleted:
            return True
        if instance["provisioned"] is provisioned:
            return True
        self.log.info("delay leader-first action on service %s", svc.svcname)
        return False

    def overloaded_up_service_instances(self, svcname):
        return [nodename for nodename in self.up_service_instances(svcname) if self.node_overloaded(nodename)]

    def scaler_slots(self, svcnames):
        count = 0
        for svcname in svcnames:
            svc = shared.SERVICES[svcname]
            if svc.topology == "flex":
                width = len([1 for nodename in svc.peers if nodename in shared.CLUSTER_DATA])
                count += min(width, svc.flex_min_nodes)
            else:
                count += 1
        return count

    def up_service_instances(self, svcname):
        nodenames = []
        for nodename, instance in self.get_service_instances(svcname).items():
            if instance["avail"] == "up":
                nodenames.append(nodename)
            elif instance["monitor"]["status"] in ("restarting", "starting", "wait children", "provisioning"):
                nodenames.append(nodename)
        return nodenames

    def peer_transitioning(self, svcname):
        """
        Return the nodename of the first peer with the service in a transition
        state.
        """
        for nodename, instance in self.get_service_instances(svcname).items():
            if nodename == rcEnv.nodename:
                continue
            if instance["monitor"]["status"].endswith("ing"):
                return nodename

    def better_peers_ready(self, svc):
        ranks = self.placement_ranks(svc, candidates=svc.peers)
        peers = []
        for nodename in ranks:
            if nodename == rcEnv.nodename:
                return peers
            instance = self.get_service_instance(svc.svcname, nodename)
            if instance is None:
                continue
            if instance["monitor"].get("status") == "ready":
                peers.append(nodename)
        return peers

    def better_peer_ready(self, svc, candidates):
        """
        Return the nodename of the first peer with the service in ready state, or
        None if we are placement leader or no peer is in ready state.
        """
        if self.placement_leader(svc, candidates):
            return
        for nodename, instance in self.get_service_instances(svc.svcname).items():
            if nodename == rcEnv.nodename:
                continue
            if instance["monitor"].get("status") == "ready":
                return nodename

    #########################################################################
    #
    # Cluster nodes aggregations
    #
    #########################################################################
    @staticmethod
    def get_clu_agg_frozen():
        fstatus = "undef"
        fstatus_l = []
        n_instances = 0
        with shared.CLUSTER_DATA_LOCK:
            for node in shared.CLUSTER_DATA.values():
                try:
                    fstatus_l.append(node.get("frozen"))
                except KeyError:
                    # sender daemon outdated
                    continue
                n_instances += 1
        n_frozen = fstatus_l.count(True)
        if n_instances == 0:
            fstatus = 'n/a'
        elif n_frozen == n_instances:
            fstatus = 'frozen'
        elif n_frozen == 0:
            fstatus = 'thawed'
        else:
            fstatus = 'mixed'
        return fstatus

    #########################################################################
    #
    # Service instances status aggregation
    #
    #########################################################################
    def get_agg_avail(self, svcname):
        try:
            instance = self.get_any_service_instance(svcname)
        except IndexError:
            instance = Storage()
        if instance is None:
            # during init for example
            return "unknown"
        topology = instance.get("topology")
        if topology == "failover":
            avail = self.get_agg_avail_failover(svcname)
        elif topology == "flex":
            avail = self.get_agg_avail_flex(svcname)
        else:
            avail = "unknown"
        slaves = instance.get("children", [])
        slaves += instance.get("slaves", [])
        slaves += instance.get("scaler_slaves", [])
        if slaves:
            avails = set([avail])
            for child in slaves:
                try:
                    child_avail = shared.AGG[child]["avail"]
                except KeyError:
                    child_avail = "unknown"
                avails.add(child_avail)
            if avails == set(["n/a"]):
                return "n/a"
            avails -= set(["n/a"])
            if len(avails) == 1:
                return list(avails)[0]
            return "warn"
        elif instance.get("scale") is not None:
            # scaler without slaves
            return "n/a"
        return avail

    def get_agg_overall(self, svcname):
        ostatus = 'undef'
        ostatus_l = []
        n_instances = 0
        for instance in self.get_service_instances(svcname).values():
            if "overall" not in instance:
                continue
            ostatus_l.append(instance["overall"])
            n_instances += 1
        ostatus_s = set(ostatus_l)

        if n_instances == 0:
            ostatus = "n/a"
        elif "warn" in ostatus_s or "stdby down" in ostatus_s:
            ostatus = "warn"
        elif len(ostatus_s) == 1:
            ostatus = ostatus_s.pop()
        elif set(["up", "down"]) == ostatus_s or \
             set(["up", "stdby up"]) == ostatus_s or \
             set(["up", "down", "stdby up"]) == ostatus_s or \
             set(["up", "down", "stdby up", "n/a"]) == ostatus_s:
            ostatus = "up"
        elif set(["down", "stdby up"]) == ostatus_s or \
             set(["down", "stdby up", "n/a"]) == ostatus_s:
            ostatus = "down"
        if "stdby" in ostatus:
            ostatus = "down"
        return ostatus

    def get_agg_frozen(self, svcname):
        frozen = 0
        total = 0
        for instance in self.get_service_instances(svcname).values():
            if "frozen" not in instance:
                # deleting instance
                continue
            if instance.get("frozen"):
                frozen += 1
            total += 1
        if total == 0:
            return "n/a"
        elif frozen == total:
            return "frozen"
        elif frozen == 0:
            return "thawed"
        else:
            return "mixed"

    def is_instance_shutdown(self, instance):
        def has_stdby(instance):
            for resource in instance["resources"].values():
                if resource.get("standby"):
                    return True
            return False
        _has_stdby = has_stdby(instance)
        if _has_stdby and instance["avail"] not in ("n/a", "stdby down") or \
           not _has_stdby and instance["avail"] not in ("n/a", "down"):
            return False
        return True

    def get_agg_shutdown(self, svcname):
        for instance in self.get_service_instances(svcname).values():
            if not self.is_instance_shutdown(instance):
                return False
        return True

    def get_agg_avail_failover(self, svcname):
        astatus_l = []
        n_instances = 0
        for instance in self.get_service_instances(svcname).values():
            if "avail" not in instance:
                continue
            astatus_l.append(instance["avail"])
            n_instances += 1
        astatus_s = set(astatus_l)

        n_up = astatus_l.count("up")
        if n_instances == 0:
            return 'n/a'
        elif astatus_s == set(['n/a']):
            return 'n/a'
        elif 'warn' in astatus_l:
            return 'warn'
        elif n_up > 1:
            return 'warn'
        elif n_up == 1:
            return 'up'
        else:
            return 'down'

    def get_agg_avail_flex(self, svcname):
        astatus_l = []
        n_instances = 0
        for instance in self.get_service_instances(svcname).values():
            if "avail" not in instance:
                continue
            astatus_l.append(instance["avail"])
            n_instances += 1
        astatus_s = set(astatus_l)

        n_up = astatus_l.count("up")
        if n_instances == 0:
            return 'n/a'
        elif astatus_s == set(['n/a']):
            return 'n/a'
        elif n_up == 0:
            return 'down'
        elif 'warn' in astatus_l:
            return 'warn'
        elif n_up > instance.get("flex_max_nodes", n_instances):
            return 'warn'
        elif n_up < instance.get("flex_min_nodes", 1):
            return 'warn'
        else:
            return 'up'

    def get_agg_placement(self, svcname):
        instances = [instance for instance in self.get_service_instances(svcname).values() \
                     if not instance.get("frozen")]
        if len(instances) < 2:
            return "optimal"
        has_up = False
        placement = "optimal"
        for instance in instances:
            try:
                leader = instance["monitor"].get("placement") == "leader"
                avail = instance["avail"]
            except KeyError:
                continue
            if avail == "up":
                has_up = True
                if not leader:
                    placement = "non-optimal"
            elif leader:
                placement = "non-optimal"
        if not has_up:
            return "n/a"
        return placement

    def get_agg_provisioned(self, svcname):
        provisioned = 0
        total = 0
        for instance in self.get_service_instances(svcname).values():
            if "provisioned" not in instance:
                continue
            total += 1
            if instance["provisioned"]:
                provisioned += 1
        if total == 0:
            return "n/a"
        elif provisioned == total:
            return True
        elif provisioned == 0:
            return False
        return "mixed"

    def get_agg_aborted(self, svcname):
        for inst in self.get_service_instances(svcname).values():
            try:
                global_expect = inst["monitor"]["global_expect"]
            except KeyError:
                global_expect = None
            if global_expect not in (None, "aborted"):
                return False
            try:
                local_expect = inst["monitor"]["local_expect"]
            except KeyError:
                local_expect = None
            if local_expect not in (None, "started"):
                return False
        return True

    def get_agg_deleted(self, svcname):
        if len([True for inst in self.get_service_instances(svcname).values() if "avail" in inst]) > 0:
            return False
        return True

    def get_agg_purged(self, provisioned, deleted):
        if deleted is False:
            return False
        if provisioned in (False, "mixed"):
            return False
        return True

    #########################################################################
    #
    # Convenience methods
    #
    #########################################################################
    def status_older_than_cf(self, svcname):
        status_age = shared.CLUSTER_DATA[rcEnv.nodename].get("services", {}).get("status", {}).get(svcname, {}).get("updated", "0")
        config_age = shared.CLUSTER_DATA[rcEnv.nodename].get("services", {}).get("config", {}).get(svcname, {}).get("updated", "0")
        return status_age < config_age

    def service_instances_frozen(self, svcname):
        """
        Return the nodenames with a frozen instance of the specified service.
        """
        return [nodename for (nodename, instance) in \
                self.get_service_instances(svcname).items() if \
                instance.get("frozen")]

    def service_instances_thawed(self, svcname):
        """
        Return the nodenames with a frozen instance of the specified service.
        """
        return [nodename for (nodename, instance) in \
                self.get_service_instances(svcname).items() if \
                not instance.get("frozen")]

    @staticmethod
    def get_local_svcnames():
        """
        Extract service instance names from the locally maintained hb data.
        """
        svcnames = []
        try:
            with shared.CLUSTER_DATA_LOCK:
                for svcname in shared.CLUSTER_DATA[rcEnv.nodename]["services"]["status"]:
                    if shared.CLUSTER_DATA[rcEnv.nodename]["services"]["status"][svcname]["avail"] == "up":
                        svcnames.append(svcname)
        except KeyError:
            return []
        return svcnames

    @staticmethod
    def get_services_configs():
        """
        Return a hash indexed by svcname and nodename, containing the services
        configuration mtime and checksum.
        """
        data = {}
        with shared.CLUSTER_DATA_LOCK:
            for nodename in shared.CLUSTER_DATA:
                try:
                    for svcname in shared.CLUSTER_DATA[nodename]["services"]["config"]:
                        if svcname not in shared.CLUSTER_DATA[nodename]["services"]["status"] or \
                           "avail" not in shared.CLUSTER_DATA[nodename]["services"]["status"][svcname]:
                            # deleting
                            continue
                        if svcname not in data:
                            data[svcname] = {}
                        data[svcname][nodename] = Storage(shared.CLUSTER_DATA[nodename]["services"]["config"][svcname])
                except KeyError:
                    pass
        return data

    @staticmethod
    def get_service_instances(svcname, discard_empty=False):
        """
        Return the specified service status structures on all nodes.
        """
        instances = {}
        with shared.CLUSTER_DATA_LOCK:
            for nodename in shared.CLUSTER_DATA:
                try:
                    if svcname in shared.CLUSTER_DATA[nodename]["services"]["status"]:
                        if discard_empty and shared.CLUSTER_DATA[nodename]["services"]["status"][svcname]:
                            continue
                        instances[nodename] = shared.CLUSTER_DATA[nodename]["services"]["status"][svcname]
                except KeyError:
                    continue
        return instances

    @staticmethod
    def get_any_service_instance(svcname):
        """
        Return the specified service status structure on any node.
        """
        with shared.CLUSTER_DATA_LOCK:
            for nodename in shared.CLUSTER_DATA:
                try:
                    if svcname in shared.CLUSTER_DATA[nodename]["services"]["status"]:
                        if shared.CLUSTER_DATA[nodename]["services"]["status"][svcname] in (None, ""):
                            continue
                        return shared.CLUSTER_DATA[nodename]["services"]["status"][svcname]
                except KeyError:
                    continue

    @staticmethod
    def get_last_svc_config(svcname):
        with shared.CLUSTER_DATA_LOCK:
            try:
                return shared.CLUSTER_DATA[rcEnv.nodename]["services"]["config"][svcname]
            except KeyError:
                return

    def wait_service_config_consensus(self, svcname, peers, timeout=60):
        if len(peers) < 2:
            return True
        self.log.info("wait for service %s consensus on config amongst peers %s",
                      svcname, ",".join(peers))
        for _ in range(timeout):
            if self.service_config_consensus(svcname, peers):
                return True
            time.sleep(1)
        self.log.error("service %s couldn't reach config consensus in %d seconds",
                       svcname, timeout)
        return False

    def service_config_consensus(self, svcname, peers):
        if len(peers) < 2:
            self.log.debug("%s auto consensus. peers: %s", svcname, peers)
            return True
        ref_csum = None
        for peer in peers:
            if peer not in shared.CLUSTER_DATA:
                # discard unreachable nodes from the consensus
                continue
            try:
                csum = shared.CLUSTER_DATA[peer]["services"]["config"][svcname]["csum"]
            except KeyError:
                #self.log.debug("service %s peer %s has no config cksum yet", svcname, peer)
                return False
            except Exception as exc:
                self.log.exception(exc)
                return False
            if ref_csum is None:
                ref_csum = csum
            if ref_csum is not None and ref_csum != csum:
                #self.log.debug("service %s peer %s has a different config cksum", svcname, peer)
                return False
        self.log.info("service %s config consensus reached", svcname)
        return True

    def get_services_config(self):
        config = {}
        for cfg in glob.glob(os.path.join(rcEnv.paths.pathetc, "*.conf")):
            svcname = os.path.basename(cfg[:-5])
            if svcname == "node":
                continue
            linkp = os.path.join(rcEnv.paths.pathetc, svcname)
            if not os.path.exists(linkp):
                continue
            try:
                mtime = os.path.getmtime(cfg)
            except Exception as exc:
                self.log.warning("failed to get %s mtime: %s", cfg, str(exc))
                mtime = 0
            mtime = datetime.datetime.utcfromtimestamp(mtime)
            last_config = self.get_last_svc_config(svcname)
            if last_config is None or mtime > datetime.datetime.strptime(last_config["updated"], shared.DATEFMT):
                self.log.info("compute service %s config checksum", svcname)
                try:
                    csum = fsum(cfg)
                except (OSError, IOError) as exc:
                    self.log.info("service %s config checksum error: %s", svcname, exc)
                    continue
                try:
                    with shared.SERVICES_LOCK:
                        shared.SERVICES[svcname] = build(svcname, node=shared.NODE)
                except Exception as exc:
                    self.log.error("%s build error: %s", svcname, str(exc))
                    continue
            else:
                csum = last_config["csum"]
            if last_config is None:
                self.log.debug("purge %s status cache" % svcname)
                shared.SERVICES[svcname].purge_status_caches()
            with shared.SERVICES_LOCK:
                scope = sorted(list(shared.SERVICES[svcname].nodes | shared.SERVICES[svcname].drpnodes))
            config[svcname] = {
                "updated": mtime.strftime(shared.DATEFMT),
                "csum": csum,
                "scope": scope,
            }

        # purge deleted services
        with shared.SERVICES_LOCK:
            for svcname in list(shared.SERVICES.keys()):
                if svcname not in config:
                    self.log.info("purge deleted service %s", svcname)
                    del shared.SERVICES[svcname]
                    try:
                        del shared.SMON_DATA[svcname]
                    except KeyError:
                        pass
                    try:
                        del shared.CLUSTER_DATA[rcEnv.nodename]["services"]["status"][svcname]
                    except KeyError:
                        pass
        return config

    def get_last_svc_status_mtime(self, svcname):
        """
        Return the mtime of the specified service configuration file on the
        local node. If unknown, return 0.
        """
        instance = self.get_service_instance(svcname, rcEnv.nodename)
        if instance is None:
            return 0
        mtime = instance["mtime"]
        if mtime is None:
            return 0
        return mtime

    def service_status_fallback(self, svcname):
        """
        Return the specified service status structure fetched from an execution
        of svcmgr -s <svcname> json status". As we arrive here when the
        status.json doesn't exist, we don't have to specify --refresh.
        """
        self.log.info("slow path service status eval: %s", svcname)
        try:
            with shared.SERVICES_LOCK:
                return shared.SERVICES[svcname].print_status_data(mon_data=False)
        except KeyboardInterrupt:
            return
        except Exception as exc:
            self.log.warning("failed to evaluate service %s status", svcname)
            return

    def get_services_status(self, svcnames):
        """
        Return the local services status data, fetching data from status.json
        caches if their mtime changed or from CLUSTER_DATA[rcEnv.nodename] if
        not.

        Also update the monitor 'local_expect' field for each service.
        """

        # purge data cached by the @cache decorator
        purge_cache()

        # this data ends up in CLUSTER_DATA[rcEnv.nodename]["services"]["status"]
        data = {}

        for svcname in svcnames:
            fpath = os.path.join(rcEnv.paths.pathvar, "services", svcname, "status.json")
            last_mtime = self.get_last_svc_status_mtime(svcname)
            try:
                mtime = os.path.getmtime(fpath)
            except Exception:
                # preserve previous status data if any (an action may be running)
                mtime = 0
            if mtime > last_mtime + 0.0001:
                try:
                    with open(fpath, 'r') as filep:
                        try:
                            data[svcname] = json.load(filep)
                            #self.log.info("service %s status reloaded", svcname)
                        except ValueError as exc:
                            # json corrupted
                            pass
                except Exception as exc:
                    # json not found
                    pass


            if svcname not in data:
                if last_mtime > 0:
                    #self.log.info("service %s status preserved", svcname)
                    data[svcname] = shared.CLUSTER_DATA[rcEnv.nodename]["services"]["status"][svcname]
                elif shared.NMON_DATA.status == "init":
                    thr = threading.Thread(target=self.service_status_fallback, args=(svcname,))
                    thr.start()
                    self.status_threads[svcname] = thr
                    data[svcname] = {}
                else:
                    data[svcname] = self.service_status_fallback(svcname)

            if svcname in self.status_threads:
                thr = self.status_threads[svcname]
                thr.join(0)
                if not thr.is_alive():
                    del self.status_threads[svcname]

            if not data[svcname]:
                del data[svcname]
                continue

            # update the frozen instance attribute
            with shared.SERVICES_LOCK:
                data[svcname]["frozen"] = shared.SERVICES[svcname].frozen()

            # embed the updated smon data
            self.set_smon_l_expect_from_status(data, svcname)
            data[svcname]["monitor"] = self.get_service_monitor(svcname)

            # forget the stonith target node if we run the service
            if data[svcname]["avail"] == "up" and "stonith" in data[svcname]["monitor"]:
                del data[svcname]["monitor"]["stonith"]

        # deleting services (still in SMON_DATA, no longer has cf).
        # emulate a status
        for svcname in set(shared.SMON_DATA.keys()) - set(svcnames):
            data[svcname] = {
                "monitor": self.get_service_monitor(svcname),
                "resources": {},
            }

        if shared.NMON_DATA.status == "init" and len(self.status_threads) == 0:
            self.set_nmon(status="idle")
        return data

    #########################################################################
    #
    # Service-specific monitor data helpers
    #
    #########################################################################
    @staticmethod
    def reset_smon_retries(svcname, rid):
        with shared.SMON_DATA_LOCK:
            if svcname not in shared.SMON_DATA:
                return
            if "restart" not in shared.SMON_DATA[svcname]:
                return
            if rid in shared.SMON_DATA[svcname].restart:
                del shared.SMON_DATA[svcname].restart[rid]
            if len(shared.SMON_DATA[svcname].restart.keys()) == 0:
                del shared.SMON_DATA[svcname].restart

    @staticmethod
    def get_smon_retries(svcname, rid):
        with shared.SMON_DATA_LOCK:
            if svcname not in shared.SMON_DATA:
                return 0
            if "restart" not in shared.SMON_DATA[svcname]:
                return 0
            if rid not in shared.SMON_DATA[svcname].restart:
                return 0
            else:
                return shared.SMON_DATA[svcname].restart[rid]

    @staticmethod
    def inc_smon_retries(svcname, rid):
        with shared.SMON_DATA_LOCK:
            if svcname not in shared.SMON_DATA:
                return
            if "restart" not in shared.SMON_DATA[svcname]:
                shared.SMON_DATA[svcname].restart = Storage()
            if rid not in shared.SMON_DATA[svcname].restart:
                shared.SMON_DATA[svcname].restart[rid] = 1
            else:
                shared.SMON_DATA[svcname].restart[rid] += 1

    def all_nodes_frozen(self):
        with shared.CLUSTER_DATA_LOCK:
             for data in shared.CLUSTER_DATA.values():
                 if not data.get("frozen", False):
                     return False
        return True

    def all_nodes_thawed(self):
        with shared.CLUSTER_DATA_LOCK:
             for data in shared.CLUSTER_DATA.values():
                 if data.get("frozen", False):
                     return False
        return True

    def set_nmon_g_expect_from_status(self):
        nmon = self.get_node_monitor()
        if nmon.global_expect is None:
            return
        if nmon.global_expect == "frozen" and self.all_nodes_frozen():
            self.log.info("node global expect is %s and is frozen",
                          nmon.global_expect)
            self.set_nmon(global_expect="unset")
        elif nmon.global_expect == "thawed" and self.all_nodes_thawed():
            self.log.info("node global expect is %s and is thawed",
                          nmon.global_expect)
            self.set_nmon(global_expect="unset")

    def set_smon_g_expect_from_status(self, svcname, smon, status):
        """
        Align global_expect with the actual service states.
        """
        if smon.global_expect is None:
            return
        instance = self.get_service_instance(svcname, rcEnv.nodename)
        if instance is None:
            return
        local_frozen = instance.get("frozen", False)
        frozen = shared.AGG[svcname].frozen
        provisioned = shared.AGG[svcname].provisioned
        deleted = self.get_agg_deleted(svcname)
        purged = self.get_agg_purged(provisioned, deleted)
        if smon.global_expect == "stopped" and status in STOPPED_STATES and \
           local_frozen:
            self.log.info("service %s global expect is %s and its global "
                          "status is %s", svcname, smon.global_expect, status)
            self.set_smon(svcname, global_expect="unset")
        elif smon.global_expect == "shutdown" and self.get_agg_shutdown(svcname) and \
           local_frozen:
            self.log.info("service %s global expect is %s and its global "
                          "status is %s", svcname, smon.global_expect, status)
            self.set_smon(svcname, global_expect="unset")
        elif smon.global_expect == "started" and \
             status in STARTED_STATES and not local_frozen:
            self.log.info("service %s global expect is %s and its global "
                          "status is %s", svcname, smon.global_expect, status)
            self.set_smon(svcname, global_expect="unset")
        elif (smon.global_expect == "frozen" and frozen == "frozen") or \
             (smon.global_expect == "thawed" and frozen == "thawed") or \
             (smon.global_expect == "unprovisioned" and provisioned is False) or \
             (smon.global_expect == "provisioned" and provisioned is True):
            self.log.info("service %s global expect is %s, already is",
                          svcname, smon.global_expect)
            self.set_smon(svcname, global_expect="unset")
        elif (smon.global_expect == "purged" and purged is True) or \
             (smon.global_expect == "deleted" and deleted is True):
            self.log.info("service %s global expect is %s, already is",
                          svcname, smon.global_expect)
            with shared.SMON_DATA_LOCK:
                del shared.SMON_DATA[svcname]
        elif smon.global_expect == "aborted" and \
             self.get_agg_aborted(svcname):
            self.log.info("service %s action aborted", svcname)
            self.set_smon(svcname, global_expect="unset")
        elif smon.global_expect == "placed" and \
             shared.AGG[svcname].placement in ("optimal", "n/a") and \
             shared.AGG[svcname].avail == "up":
            self.set_smon(svcname, global_expect="unset")

    def set_smon_l_expect_from_status(self, data, svcname):
        if svcname not in data:
            return
        with shared.SMON_DATA_LOCK:
            if svcname not in shared.SMON_DATA:
                return
            if data[svcname]["avail"] == "up" and \
               shared.SMON_DATA[svcname].global_expect is None and \
               shared.SMON_DATA[svcname].status == "idle" and \
               shared.SMON_DATA[svcname].local_expect != "started":
                self.log.info("service %s monitor local_expect change "
                              "%s => %s", svcname,
                              shared.SMON_DATA[svcname].local_expect, "started")
                shared.SMON_DATA[svcname].local_expect = "started"

    def update_cluster_data(self):
        """
        Rescan services config and status.
        """
        data = shared.CLUSTER_DATA[rcEnv.nodename]
        data["stats"] = shared.NODE.stats()
        data["frozen"] = self.freezer.node_frozen()
        data["env"] = shared.NODE.env
        data["min_avail_mem"] = shared.NODE.min_avail_mem
        data["min_avail_swap"] = shared.NODE.min_avail_swap
        data["services"]["config"] = self.get_services_config()
        data["services"]["status"] = self.get_services_status(data["services"]["config"].keys())

        # purge deleted service instances
        for svcname in list(data["services"]["status"].keys()):
            if svcname not in data["services"]["config"]:
                self.log.debug("purge deleted service %s from status data", svcname)
                try:
                    del data["services"]["status"][svcname]
                except KeyError:
                    pass

    def update_hb_data(self):
        """
        Preapre the heartbeat data we send to other nodes.
        """
        now = time.time()

        if len(shared.LOCAL_GEN) == 0:
            return

        if self.mon_changed():
            self.update_cluster_data()
        data = shared.CLUSTER_DATA[rcEnv.nodename]

        for key in ("updated", "gen"):
            # exclude from the diff
            try:
                del data[key]
            except KeyError:
                pass

        if self.last_node_data is not None:
            diff = json_delta.diff(
                self.last_node_data, data,
                verbose=False, array_align=False, compare_lengths=False
            )
        else:
            # first run
            self.last_node_data = json.loads(json.dumps(data))
            data["gen"] = self.get_gen(inc=True)
            data["updated"] = now
            return

        if len(diff) == 0:
            data["gen"] = self.get_gen(inc=False)
            data["updated"] = now
            return

        self.last_node_data = json.loads(json.dumps(data))
        data["gen"] = self.get_gen(inc=True)
        data["updated"] = now
        diff.append([["updated"], data["updated"]])
        shared.GEN_DIFF[shared.GEN] = diff
        shared.EVENT_Q.put({
            "nodename": rcEnv.nodename,
            "kind": "patch",
            "data": diff,
        })
        self.purge_log()
        with shared.HB_MSG_LOCK:
             # reset the full status cache. get_message() will refill if
             # needed.
             shared.HB_MSG = None
             shared.HB_MSG_LEN = 0
        shared.wake_heartbeat_tx()

    def merge_hb_data(self):
        self.merge_hb_data_compat()
        self.merge_hb_data_monitor()
        self.merge_hb_data_provision()

    def merge_hb_data_compat(self):
        compat = [data.get("compat") for data in shared.CLUSTER_DATA.values() if "compat" in data]
        new_compat = len(set(compat)) <= 1
        if self.compat != new_compat:
            if new_compat:
                self.log.info("cluster members run compatible versions. "
                              "enable ha orchestration")
            else:
                self.log.warning("cluster members run incompatible versions. "
                                 "disable ha orchestration")
            self.compat = new_compat

    def merge_hb_data_monitor(self):
        """
        Set the global expect received through heartbeats as local expect, if
        the service instance is not already in the expected status.
        """
        with shared.CLUSTER_DATA_LOCK:
            nodenames = list(shared.CLUSTER_DATA.keys())
        if rcEnv.nodename not in nodenames:
            return
        nodenames.remove(rcEnv.nodename)
        with shared.CLUSTER_DATA_LOCK:
            # merge node monitors
            for nodename in nodenames:
                try:
                    global_expect = shared.CLUSTER_DATA[nodename]["monitor"].get("global_expect")
                except KeyError:
                    # sender daemon is outdated
                    continue
                if global_expect is None:
                    continue
                local_frozen = shared.CLUSTER_DATA[rcEnv.nodename].get("frozen", False)
                if (global_expect == "frozen" and not local_frozen) or \
                   (global_expect == "thawed" and local_frozen):
                    self.log.info("node %s wants local node %s", nodename, global_expect)
                    self.set_nmon(global_expect=global_expect)
                #else:
                #    self.log.info("node %s wants local node %s, already is", nodename, global_expect)

            # merge every service monitors
            for svcname, instance in shared.CLUSTER_DATA[rcEnv.nodename]["services"]["status"].items():
                current_global_expect = instance["monitor"].get("global_expect")
                if current_global_expect == "aborted":
                    # refuse a new global expect if aborting
                    continue
                for nodename in nodenames:
                    rinstance = self.get_service_instance(svcname, nodename)
                    if rinstance is None:
                        continue
                    if rinstance.get("stonith") is True:
                        self.set_smon(svcname, stonith=nodename)
                    global_expect = rinstance["monitor"].get("global_expect")
                    if global_expect is None:
                        continue
                    if svcname in shared.SERVICES and shared.SERVICES[svcname].disabled and \
                       global_expect not in ("frozen", "thawed", "aborted", "deleted"):
                        continue
                    if global_expect == current_global_expect:
                        self.log.debug("node %s wants service %s %s, already targeting that",
                                       nodename, svcname, global_expect)
                        continue
                    if self.accept_g_expect(svcname, instance, global_expect):
                        self.log.info("node %s wants service %s %s", nodename, svcname, global_expect)
                        self.set_smon(svcname, global_expect=global_expect)
                    #else:
                    #    self.log.info("node %s wants service %s %s, already is", nodename, svcname, global_expect)

    def accept_g_expect(self, svcname, instance, global_expect):
        if svcname not in shared.AGG:
            return False
        if global_expect == "stopped":
            local_avail = instance["avail"]
            local_frozen = instance.get("frozen", False)
            if local_avail not in STOPPED_STATES or not local_frozen:
                return True
            else:
                return False
        elif global_expect == "shutdown":
            return not self.get_agg_shutdown(svcname)
        elif global_expect == "started":
            status = shared.AGG[svcname].avail
            local_frozen = instance.get("frozen", False)
            if status not in STARTED_STATES or local_frozen:
                return True
            else:
                return False
        elif global_expect == "frozen":
            frozen = shared.AGG[svcname].frozen
            if frozen != "frozen":
                return True
            else:
                return False
        elif global_expect == "thawed":
            frozen = shared.AGG[svcname].frozen
            if frozen != "thawed":
                 return True
            else:
                return False
        elif global_expect == "provisioned":
            provisioned = shared.AGG[svcname].provisioned
            if provisioned is not True:
                return True
            else:
                return False
        elif global_expect == "unprovisioned":
            provisioned = shared.AGG[svcname].provisioned
            if provisioned is not False:
                return True
            else:
                return False
        elif global_expect == "deleted":
            deleted = self.get_agg_deleted(svcname)
            if deleted is False:
                return True
            else:
                return False
        elif global_expect == "purged":
            provisioned = shared.AGG[svcname].provisioned
            deleted = self.get_agg_deleted(svcname)
            purged = self.get_agg_purged(provisioned, deleted)
            if purged is False:
                return True
            else:
                return False
        elif global_expect == "aborted":
            aborted = self.get_agg_aborted(svcname)
            if aborted is False:
                return True
            else:
                return False
        elif global_expect == "placed":
            placement = shared.AGG[svcname].placement
            if placement == "non-optimal":
                return True
            else:
                return False
        return False

    def merge_hb_data_provision(self):
        """
        Merge the resource provisioned state from the peer with the most
        up-to-date change time.
        """
        with shared.SERVICES_LOCK:
            with shared.CLUSTER_DATA_LOCK:
                for svc in shared.SERVICES.values():
                    changed = False
                    for resource in svc.shared_resources:
                        try:
                            local = Storage(shared.CLUSTER_DATA[rcEnv.nodename]["services"]["status"][svc.svcname]["resources"][resource.rid]["provisioned"])
                        except KeyError:
                            local = Storage()
                        for nodename in svc.peers:
                            if nodename == rcEnv.nodename:
                                continue
                            try:
                                remote = Storage(shared.CLUSTER_DATA[nodename]["services"]["status"][svc.svcname]["resources"][resource.rid]["provisioned"])
                            except KeyError:
                                continue
                            if remote is None or remote.state is None or remote.mtime is None:
                                continue
                            elif local.mtime is None or remote.mtime > local.mtime + 0.00001:
                                self.log.info("switch %s.%s provisioned flag to %s (merged from %s)",
                                              svc.svcname, resource.rid, str(remote.state),
                                              nodename)
                                resource.write_is_provisioned_flag(remote.state, remote.mtime)
                                try:
                                    shared.CLUSTER_DATA[rcEnv.nodename]["services"]["status"][svc.svcname]["resources"][resource.rid]["provisioned"]["mtime"] = remote.mtime
                                except KeyError:
                                    continue
                                changed = True
                    if changed:
                        svc.purge_status_data_dump()
                        try:
                            svc.print_status_data_eval()
                        except Exception:
                            # can happen when deleting the service
                            pass

    def service_provisioned(self, instance):
        return instance.get("provisioned")

    def service_unprovisioned(self, instance):
        for resource in instance["resources"].values():
            if resource.get("provisioned", {}).get("state") is True:
                return False
        return True

    def get_agg(self, svcname):
        data = Storage()
        data.avail = self.get_agg_avail(svcname)
        data.frozen = self.get_agg_frozen(svcname)
        data.overall = self.get_agg_overall(svcname)
        data.placement = self.get_agg_placement(svcname)
        data.provisioned = self.get_agg_provisioned(svcname)
        with shared.AGG_LOCK:
            shared.AGG[svcname] = data
        return data

    def get_agg_services(self):
        svcnames = set()
        with shared.CLUSTER_DATA_LOCK:
            for nodename, data in shared.CLUSTER_DATA.items():
                try:
                    for svcname in data["services"]["config"]:
                        svcnames.add(svcname)
                except KeyError:
                    continue
        data = {}
        for svcname in svcnames:
            data[svcname] = self.get_agg(svcname)
        return data

    def status(self, **kwargs):
        if kwargs.get("refresh"):
            self.update_hb_data()
        data = shared.OsvcThread.status(self, **kwargs)
        with shared.CLUSTER_DATA_LOCK:
            data.nodes = dict(shared.CLUSTER_DATA)
        data["compat"] = self.compat
        data["transitions"] = self.transition_count()
        data["frozen"] = self.get_clu_agg_frozen()
        data["services"] = self.get_agg_services()
        return data

    def merge_frozen(self):
        """
        This method is only called during the rejoin grace period.

        It freezes the local services instances for services that have
        a live remote instance frozen. This prevents a node
        rejoining the cluster from taking over services that where frozen
        and stopped while we were not alive.
        """
        if self.rejoin_grace_period_expired:
            return
        for svc in shared.SERVICES.values():
            if svc.orchestrate == "no":
                continue
            if len(svc.peers) < 2:
                continue
            if self.service_frozen(svc.svcname):
                continue
            for peer in svc.peers:
                if peer == rcEnv.nodename:
                    continue
                smon = self.get_service_monitor(svc.svcname)
                if smon.global_expect == "thawed":
                    continue
                try:
                    frozen = shared.CLUSTER_DATA[peer]["services"]["status"][svc.svcname].get("frozen", False)
                except:
                    continue
                if frozen:
                    self.log.info("merge service '%s' frozen state from node '%s'",
                                  svc.svcname, peer)
                    svc.freezer.freeze()

    def service_frozen(self, svcname):
        try:
            return shared.CLUSTER_DATA[rcEnv.nodename]["services"]["status"][svcname]["frozen"]
        except:
            return

0707010005c8dc000081b40000000000000000000000015ab21754000017c6000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/osvcd_scheduler.py """
Scheduler Thread
"""
import os
import sys
import logging
import time
import datetime
from subprocess import Popen, PIPE

import osvcd_shared as shared
import rcExceptions as ex
from rcGlobalEnv import rcEnv

class Scheduler(shared.OsvcThread):
    interval = 60
    delayed = {}
    running = set()

    def max_tasks(self):
        if self.node_overloaded():
            return 2
        else:
            return shared.NODE.max_parallel

    def status(self, **kwargs):
        data = shared.OsvcThread.status(self, **kwargs)
        data["running"] = len(self.running)
        data["delayed"] = [{
            "cmd": " ".join(entry["cmd"]),
            "queued": entry["queued"].strftime(shared.JSON_DATEFMT),
            "expire": entry["expire"].strftime(shared.JSON_DATEFMT),
        } for entry in self.delayed.values()]
        return data

    def run(self):
        self.log = logging.getLogger(rcEnv.nodename+".osvcd.scheduler")
        self.log.info("scheduler started")
        if hasattr(os, "devnull"):
            devnull = os.devnull
        else:
            devnull = "/dev/null"
        self.devnull = os.open(devnull, os.O_RDWR)

        while True:
            try:
                self.do()
            except Exception as exc:
                self.log.exception(exc)
            if self.stopped():
                self.terminate_procs()
                sys.exit(0)

    def do(self):
        self.reload_config()
        last = time.time()
        done = 0
        while True:
            now = time.time()
            if done == 0:
                with shared.SCHED_TICKER:
                    shared.SCHED_TICKER.wait(1)
            if self.stopped():
                break
            self.dequeue_actions()
            if last + self.interval < now:
                last = time.time()
                self.run_scheduler()
            done = self.janitor_procs()

    def drop_running(self, sig):
        self.running -= set([sig])

    def exec_action(self, sig, cmd):
        kwargs = dict(stdout=self.devnull, stderr=self.devnull,
                      stdin=self.devnull, close_fds=os.name!="nt")
        try:
            proc = Popen(cmd, **kwargs)
        except KeyboardInterrupt:
            return
        self.running.add(sig)
        self.push_proc(proc=proc,
                       on_success="drop_running",
                       on_success_args=[sig],
                       on_error="drop_running",
                       on_error_args=[sig])

    def queue_action(self, cmd, delay, sig):
        if sig in self.delayed:
            self.log.debug("drop already queued action '%s'", " ".join(cmd))
            self.log.debug("drop already queued sig '%s'", sig)
            return []
        if sig in self.running:
            self.log.debug("drop already running action '%s'", " ".join(cmd))
            return []
        now = datetime.datetime.utcnow()
        exp = now + datetime.timedelta(seconds=delay)
        self.delayed[sig] = {
            "queued": now,
            "expire": exp,
            "cmd": cmd,
        }
        if delay == 0:
            self.log.debug("queued action '%s' for run asap" % ' '.join(cmd))
        else:
            self.log.debug("queued action '%s' delayed until %s" % (' '.join(cmd), exp))
        return []

    def dequeue_actions(self):
        now = datetime.datetime.utcnow()
        dequeued = []
        to_run = [sig for sig, task in self.delayed.items() if task["expire"] < now]
        to_run = sorted(to_run, key=lambda sig: self.delayed[sig]["queued"])
        open_slots = max(self.max_tasks() - len(self.running), 0)
        for sig in to_run[:open_slots]:
            self.log.info("run '%s' queued at %s",
                          " ".join(self.delayed[sig]["cmd"]),
                          self.delayed[sig]["queued"])
            self.exec_action(sig, self.delayed[sig]["cmd"])
            dequeued.append(sig)
        for sig in dequeued:
            del self.delayed[sig]

    def run_scheduler(self):
        #self.log.info("run schedulers")
        nonprov = []
        run = []

        if shared.NODE:
            shared.NODE.options.cron = True
            for action in shared.NODE.sched.scheduler_actions:
                try:
                    delay = shared.NODE.sched.validate_action(action)
                except ex.excAbortAction:
                    continue
                cmd = [rcEnv.paths.nodemgr, action, "--cron"]
                sig = ":".join(["node", action])
                run += self.queue_action(cmd, delay, sig)
        for svcname in list(shared.SERVICES.keys()):
            try:
                svc = shared.SERVICES[svcname]
            except KeyError:
                # deleted during previous iterations
                continue
            svc.options.cron = True
            try:
                provisioned = shared.AGG[svc.svcname].provisioned
            except KeyError:
                continue
            if provisioned is not True:
                nonprov.append(svc.svcname)
                continue
            for action in svc.sched.scheduler_actions:
                try:
                    data = svc.sched.validate_action(action)
                except ex.excAbortAction:
                    continue
                cmd = [rcEnv.paths.svcmgr, "-s", svc.svcname, action, "--cron", "--waitlock=5"]
                try:
                    rids, delay = data
                    rids = ','.join(rids)
                    sig = ":".join(["svc", svc.svcname, action, rids])
                    cmd += ["--rid", rids]
                except TypeError:
                    delay = data
                    sig = ":".join(["svc", svc.svcname, action])
                run += self.queue_action(cmd, delay, sig)

        # log a scheduler loop digest
        msg = []
        if len(nonprov) > 0:
            msg.append("non provisioned service skipped: %s." % ", ".join(nonprov))
        if len(run) > 0:
            msg.append("ran: %s." % ", ".join(run))
        if len(msg) > 0:
            self.log.info(" ".join(msg))

  0707010005c8dd000081b40000000000000000000000015ab2175400007c17000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/osvcd_shared.py    """
A module to share variables used by osvcd threads.
"""
import os
import sys
import threading
import datetime
import time
import codecs
import hashlib
from subprocess import Popen, PIPE

try:
    import Queue as queue
except ImportError:
    import queue

import rcExceptions as ex
from rcConfigParser import RawConfigParser
from rcUtilities import lazy, unset_lazy, is_string
from rcGlobalEnv import rcEnv, Storage
from freezer import Freezer
from converters import convert_duration, convert_boolean

# disable orchestration if a peer announces a different compat version than ours
COMPAT_VERSION = 5

# the event queue to feed to clients listening for changes
EVENT_Q = queue.Queue()

# current generation of the dataset on the local node
GEN = 0

# track the generation of the local dataset on peer nodes
LOCAL_GEN = {}

# track the generation of the peer datasets we merged
REMOTE_GEN = {}

# track the local dataset gen diffs pending merge by peers
GEN_DIFF = {}

DATEFMT = "%Y-%m-%dT%H:%M:%S.%fZ"
JSON_DATEFMT = "%Y-%m-%dT%H:%M:%SZ"
MAX_MSG_SIZE = 1024 * 1024

# The threads store
THREADS = {}
THREADS_LOCK = threading.RLock()

# A node object instance. Used to access node properties and methods.
NODE = None
NODE_LOCK = threading.RLock()

# CRM services objects. Used to access services properties.
# The monitor thread reloads a new Svc object when the corresponding
# configuration file changes.
SERVICES = {}
SERVICES_LOCK = threading.RLock()

# Per service aggretated data of instances.
# Refresh on monitor status eval, and embedded in the returned data
AGG = {}
AGG_LOCK = threading.RLock()

# The encrypted message all the heartbeat tx threads send.
# It is refreshed in the monitor thread loop.
HB_MSG = None
HB_MSG_LEN = 0
HB_MSG_LOCK = threading.RLock()

# the local service monitor data, where the listener can set expected states
SMON_DATA = {}
SMON_DATA_LOCK = threading.RLock()

# the local node monitor data, where the listener can set expected states
NMON_DATA = Storage({
    "status": "idle",
    "status_updated": time.time(),
})
NMON_DATA_LOCK = threading.RLock()

# a boolean flag used to signal the monitor it has to do the long loop asap
MON_CHANGED = []

# The per-threads configuration, stats and states store
# The monitor thread states include cluster-wide aggregated data
CLUSTER_DATA = {}
CLUSTER_DATA_LOCK = threading.RLock()

# thread loop conditions and helpers
DAEMON_STOP = threading.Event()
MON_TICKER = threading.Condition()
COLLECTOR_TICKER = threading.Condition()
SCHED_TICKER = threading.Condition()
HB_TX_TICKER = threading.Condition()

# a queue of xmlrpc calls to do, fed by the lsnr, purged by the collector thread
COLLECTOR_XMLRPC_QUEUE = []

def wake_heartbeat_tx():
    """
    Notify the heartbeat tx thread to do they periodic job immediatly
    """
    with HB_TX_TICKER:
        HB_TX_TICKER.notify_all()

def wake_monitor(reason="unknown", immediate=False):
    """
    Notify the monitor thread to do they periodic job immediatly
    """
    global MON_CHANGED
    with MON_TICKER:
        MON_CHANGED.append(reason)
        if immediate:
            MON_TICKER.notify_all()

def wake_collector():
    """
    Notify the scheduler thread to do they periodic job immediatly
    """
    with COLLECTOR_TICKER:
        COLLECTOR_TICKER.notify_all()

def wake_scheduler():
    """
    Notify the scheduler thread to do they periodic job immediatly
    """
    with SCHED_TICKER:
        SCHED_TICKER.notify_all()

#############################################################################
#
# Base Thread class
#
#############################################################################
class OsvcThread(threading.Thread):
    """
    Thread class with a stop() method. The thread itself has to check
    regularly for the stopped() condition.
    """
    stop_tmo = 60

    def __init__(self):
        super(OsvcThread, self).__init__()
        self._stop_event = threading.Event()
        self._node_conf_event = threading.Event()
        self.created = time.time()
        self.threads = []
        self.procs = []

        # hash for log dups avoiding
        self.duplog_data = {}

    def notify_config_change(self):
        """
        Notify thread the node configuration file changed.
        """
        self._node_conf_event.set()

    def stop(self):
        """
        Notify thread they need to stop.
        """
        self._stop_event.set()

    def unstop(self):
        """
        Notify daemon it is free to restart the thread.
        """
        self._stop_event.clear()

    def stopped(self):
        """
        Return True if the thread excepted state is stopped.
        """
        return self._stop_event.is_set()

    def status(self, **kwargs):
        """
        Return the thread status data structure to embed in the 'daemon
        status' data.
        """
        if self.stopped():
            if self.is_alive():
                state = "stopping"
            else:
                state = "stopped"
        else:
            if self.is_alive():
                state = "running"
            else:
                state = "terminated"
        data = Storage({
            "state": state,
            "created": datetime.datetime.utcfromtimestamp(self.created)\
                               .strftime(JSON_DATEFMT),
        })
        return data

    def push_proc(self, proc,
                  on_success=None, on_success_args=None, on_success_kwargs=None,
                  on_error=None, on_error_args=None, on_error_kwargs=None):
        """
        Enqueue a structure including a Popen() result and the success and
        error callbacks.
        """
        self.procs.append(Storage({
            "proc": proc,
            "on_success": on_success,
            "on_success_args": on_success_args if on_success_args else [],
            "on_success_kwargs": on_success_kwargs if on_success_kwargs else {},
            "on_error": on_error,
            "on_error_args": on_error_args if on_error_args else [],
            "on_error_kwargs": on_error_kwargs if on_error_kwargs else {},
        }))

    def terminate_procs(self):
        """
        Send a terminate() to all procs in the queue and wait for their
        completion.
        """
        for data in self.procs:
            data.proc.terminate()
            for _ in range(self.stop_tmo):
                data.proc.poll()
                if data.proc.returncode is not None:
                    data.proc.communicate()
                    break
                time.sleep(1)

    def janitor_procs(self):
        done = []
        for idx, data in enumerate(self.procs):
            data.proc.poll()
            if data.proc.returncode is not None:
                data.proc.communicate()
                done.append(idx)
                if data.proc.returncode == 0 and data.on_success:
                    getattr(self, data.on_success)(*data.on_success_args,
                                                   **data.on_success_kwargs)
                elif data.proc.returncode != 0 and data.on_error:
                    getattr(self, data.on_error)(*data.on_error_args,
                                                 **data.on_error_kwargs)
        for idx in sorted(done, reverse=True):
            del self.procs[idx]
        return len(done)

    def join_threads(self):
        for thr in self.threads:
            thr.join()

    def janitor_threads(self):
        done = []
        for idx, thr in enumerate(self.threads):
            thr.join(0)
            if not thr.is_alive():
                done.append(idx)
        for idx in sorted(done, reverse=True):
            del self.threads[idx]
        if len(self.threads) > 2:
            self.log.debug("threads queue length %d", len(self.threads))

    @lazy
    def freezer(self):
        return Freezer("node")

    @lazy
    def config(self):
        try:
            config = RawConfigParser()
            with codecs.open(rcEnv.paths.nodeconf, "r", "utf8") as filep:
                if sys.version_info[0] >= 3:
                    config.read_file(filep)
                else:
                    config.readfp(filep)
        except Exception as exc:
            self.log.info("error loading config: %s", exc)
            raise ex.excAbortAction()
        return config

    def reload_config(self):
        if not self._node_conf_event.is_set():
            return
        self._node_conf_event.clear()
        self.log.info("config change event received")
        unset_lazy(self, "config")
        unset_lazy(self, "quorum")
        unset_lazy(self, "arbitrators")
        unset_lazy(self, "cluster_name")
        unset_lazy(self, "cluster_key")
        unset_lazy(self, "cluster_id")
        unset_lazy(self, "cluster_nodes")
        unset_lazy(self, "sorted_cluster_nodes")
        unset_lazy(self, "maintenance_grace_period")
        unset_lazy(self, "rejoin_grace_period")
        unset_lazy(self, "ready_period")
        if not hasattr(self, "reconfigure"):
            return
        try:
            getattr(self, "reconfigure")()
        except Exception as exc:
            self.log.error("reconfigure error: %s", str(exc))
            self.stop()

    @staticmethod
    def get_service(svcname):
        with SERVICES_LOCK:
            if svcname not in SERVICES:
                return
        return SERVICES[svcname]

    @staticmethod
    def get_services_nodenames():
        """
        Return the services nodes and drpnodes name, fetching the information
        from the Svc objects.
        """
        nodenames = set()
        with SERVICES_LOCK:
            for svc in SERVICES.values():
                nodenames |= svc.nodes | svc.drpnodes
        return nodenames

    def set_nmon(self, status=None, local_expect=None, global_expect=None):
        global NMON_DATA
        with NMON_DATA_LOCK:
            if status:
                if status != NMON_DATA.status:
                    self.log.info(
                        "node monitor status change: %s => %s",
                        NMON_DATA.status if \
                            NMON_DATA.status else "none",
                        status
                    )
                NMON_DATA.status = status
                NMON_DATA.status_updated = time.time()

            if local_expect:
                if local_expect == "unset":
                    local_expect = None
                if local_expect != NMON_DATA.local_expect:
                    self.log.info(
                        "node monitor local expect change: %s => %s",
                        NMON_DATA.local_expect if \
                            NMON_DATA.local_expect else "none",
                        local_expect
                    )
                NMON_DATA.local_expect = local_expect

            if global_expect:
                if global_expect == "unset":
                    global_expect = None
                if global_expect != NMON_DATA.global_expect:
                    self.log.info(
                        "node monitor global expect change: %s => %s",
                        NMON_DATA.global_expect if \
                            NMON_DATA.global_expect else "none",
                        global_expect
                    )
                NMON_DATA.global_expect = global_expect

        wake_monitor(reason="node mon change")

    def set_smon(self, svcname, status=None, local_expect=None,
                 global_expect=None, reset_retries=False,
                 stonith=None):
        global SMON_DATA
        with SMON_DATA_LOCK:
            if svcname not in SMON_DATA:
                SMON_DATA[svcname] = Storage({
                    "status": "idle",
                    "status_updated": time.time(),
                })
            if status:
                if status != SMON_DATA[svcname].status:
                    self.log.info(
                        "service %s monitor status change: %s => %s",
                        svcname,
                        SMON_DATA[svcname].status if \
                            SMON_DATA[svcname].status else "none",
                        status
                    )
                SMON_DATA[svcname].status = status
                SMON_DATA[svcname].status_updated = time.time()

            if local_expect:
                if local_expect == "unset":
                    local_expect = None
                if local_expect != SMON_DATA[svcname].local_expect:
                    self.log.info(
                        "service %s monitor local expect change: %s => %s",
                        svcname,
                        SMON_DATA[svcname].local_expect if \
                            SMON_DATA[svcname].local_expect else "none",
                        local_expect
                    )
                SMON_DATA[svcname].local_expect = local_expect

            if global_expect:
                if global_expect == "unset":
                    global_expect = None
                if global_expect != SMON_DATA[svcname].global_expect:
                    self.log.info(
                        "service %s monitor global expect change: %s => %s",
                        svcname,
                        SMON_DATA[svcname].global_expect if \
                            SMON_DATA[svcname].global_expect else "none",
                        global_expect
                    )
                SMON_DATA[svcname].global_expect = global_expect

            if reset_retries and "restart" in SMON_DATA[svcname]:
                self.log.info("service %s monitor resources restart count reset",
                              svcname)
                del SMON_DATA[svcname]["restart"]

            if stonith:
                if stonith == "unset":
                    stonith = None
                if stonith != SMON_DATA[svcname].stonith:
                    self.log.info(
                        "service %s monitor stonith change: %s => %s",
                        svcname,
                        SMON_DATA[svcname].stonith if \
                            SMON_DATA[svcname].stonith else "none",
                        stonith
                    )
                SMON_DATA[svcname].stonith = stonith
        wake_monitor(reason="service %s mon change" % svcname)

    def get_node_monitor(self, nodename=None):
        """
        Return the Monitor data of the node.
        """
        if nodename is None:
            with NMON_DATA_LOCK:
                data = Storage(NMON_DATA)
        else:
            with CLUSTER_DATA_LOCK:
                if nodename not in CLUSTER_DATA:
                    return
                data = Storage(CLUSTER_DATA[nodename].get("monitor", {}))
        return data

    def get_service_monitor(self, svcname):
        """
        Return the Monitor data of a service.
        """
        with SMON_DATA_LOCK:
            if svcname not in SMON_DATA:
                self.set_smon(svcname, "idle")
            data = Storage(SMON_DATA[svcname])
            data["placement"] = self.get_service_placement(svcname)
            return data

    def get_service_placement(self, svcname):
        with SERVICES_LOCK:
            if svcname not in SERVICES:
                return ""
            svc = SERVICES[svcname]
            if self.placement_leader(svc, silent=True):
                return "leader"
        return ""

    def node_command(self, cmd):
        """
        A generic nodemgr command Popen wrapper.
        """
        cmd = [rcEnv.paths.nodemgr] + cmd
        self.log.info("execute: %s", " ".join(cmd))
        proc = Popen(cmd, stdout=None, stderr=None, stdin=None, close_fds=True)
        return proc

    def service_command(self, svcname, cmd, stdin=None):
        """
        A generic svcmgr command Popen wrapper.
        """
        env = os.environ.copy()
        env["OSVC_ACTION_ORIGIN"] = "daemon"
        cmd = [rcEnv.paths.svcmgr, '-s', svcname, "--local"] + cmd
        self.log.info("execute: %s", " ".join(cmd))
        if stdin is not None:
            _stdin = PIPE
        else:
            _stdin = None
        proc = Popen(cmd, stdout=None, stderr=None, stdin=_stdin, close_fds=True, env=env)
        if stdin:
            proc.stdin.write(stdin.encode())
        return proc

    def add_cluster_node(self, nodename):
        nodes = " ".join(sorted(list(set(self.cluster_nodes + [nodename]))))
        cmd = ["set", "--param", "cluster.nodes", "--value", nodes]
        proc = self.node_command(cmd)
        ret = proc.wait()
        return ret

    def remove_cluster_node(self, nodename):
        cmd = ["set", "--param", "cluster.nodes", "--remove", nodename]
        proc = self.node_command(cmd)
        return proc.wait()

    @lazy
    def quorum(self):
        if self.config.has_option("cluster", "quorum"):
            return convert_boolean(self.config.get("cluster", "quorum"))
        else:
            return False

    @lazy
    def maintenance_grace_period(self):
        if self.config.has_option("node", "maintenance_grace_period"):
            return convert_duration(self.config.get("node", "maintenance_grace_period"))
        else:
            return 60

    @lazy
    def rejoin_grace_period(self):
        if self.config.has_option("node", "rejoin_grace_period"):
            return convert_duration(self.config.get("node", "rejoin_grace_period"))
        else:
            return 90

    @lazy
    def ready_period(self):
        if self.config.has_option("node", "ready_period"):
            return convert_duration(self.config.get("node", "ready_period"))
        else:
            return 5

    def in_maintenance_grace_period(self, nmon):
        if nmon.status in ("upgrade", "init"):
            return True
        if nmon.status == "maintenance" and \
           nmon.status_updated > time.time() - self.maintenance_grace_period:
            return True
        return False

    def split_handler(self):
        if not self.quorum:
            self.duplog("info", "cluster is split, ignore as cluster.quorum is "
                        "false", msgid="quorum disabled")
            return
        if self.freezer.node_frozen():
            self.duplog("info", "cluster is split, ignore as the node is frozen",
                        msgid="quorum disabled")
            return
        live = len(CLUSTER_DATA)
        total = len(self.cluster_nodes)
        if live > total / 2:
            self.duplog("info", "cluster is split, we have 1st ring quorum: "
                        "%(live)d/%(total)d nodes", live=live, total=total)
            return
        arbitrator_vote = 0
        for arbitrator in self.arbitrators:
            ret = NODE._ping(arbitrator["name"])
            if ret == 0:
                arbitrator_vote = 1
                break
        if live + arbitrator_vote > total / 2:
            self.duplog("info", "cluster is split, we have 2nd ring quorum: "
                        "%(live)d+%(avote)d/%(total)d nodes (%(a)s)",
                        live=live, avote=arbitrator_vote, total=total,
                        a=arbitrator["name"])
            return
        self.duplog("info", "cluster is split, we don't have 1st nor 2nd ring "
                    "quorum: %(live)d+%(avote)d/%(total)d nodes (%(a)s)",
                    live=live, avote=arbitrator_vote, total=total,
                    a=arbitrator["name"])
        self.log.info("toc")
        NODE.system.crash()

    def forget_peer_data(self, nodename, change=False):
        """
        Purge a stale peer data if all rx threads are down.
        """
        nmon = self.get_node_monitor(nodename=nodename)
        if nmon is None:
            return
        if not self.peer_down(nodename):
            if change:
                self.log.info("other rx threads still receive from node %s",
                              nodename)
            return
        if self.in_maintenance_grace_period(nmon):
            if change:
                self.log.info("preserve node %s data in %s since %d (grace %s)",
                              nodename, nmon.status, time.time()-nmon.status_updated,
                              self.maintenance_grace_period)
            return
        self.log.info("no rx thread still receive from node %s and maintenance "
                      "grace period expired. flush its data",
                      nodename)
        with CLUSTER_DATA_LOCK:
            try:
                del CLUSTER_DATA[nodename]
            except KeyError:
                pass
            try:
                del LOCAL_GEN[nodename]
            except KeyError:
                pass
            try:
                # will ask for a full when the node comes back again
                del REMOTE_GEN[nodename]
            except KeyError:
                pass
        wake_monitor(reason="forget node %s data" % nodename)
        self.split_handler()

    def peer_down(self, nodename):
        """
        Return True if no rx threads receive data from the specified peer
        node.
        """
        with THREADS_LOCK:
            for thr_id, thread in THREADS.items():
                if not thr_id.endswith(".rx"):
                    continue
                rx_status = thread.status()
                try:
                    peer_status = rx_status["peers"][nodename]["beating"]
                except KeyError:
                    continue
                if peer_status:
                    return False
        return True

    @staticmethod
    def get_service_instance(svcname, nodename):
        """
        Return the specified service status structure on the specified node.
        """
        try:
            with CLUSTER_DATA_LOCK:
                return Storage(CLUSTER_DATA[nodename]["services"]["status"][svcname])
        except KeyError:
            return

    #########################################################################
    #
    # Placement policies
    #
    #########################################################################
    def placement_candidates(self, svc, discard_frozen=True,
                             discard_overloaded=True,
                             discard_preserved=True,
                             discard_unprovisioned=True,
                             discard_constraints_violation=True,
                             discard_start_failed=True):
        """
        Return the list of service nodes meeting the following criteria:
        * we have valid service instance data (not unknown, has avail)
        * the node is not in maintenance, init or upgrade (default)
        * the node is not frozen (default)
        * the node is not overloaded (default)
        * the service is not frozen (default)
        * the service instance is provisioned (default)
        * the service instance smon status is not "start failed" (default)
        * the service instance constraints are eval'ed True (default)
        """
        candidates = []
        with CLUSTER_DATA_LOCK:
            for nodename, data in CLUSTER_DATA.items():
                if data == "unknown":
                    continue
                if discard_preserved and data.get("monitor", {}).get("status") in ("maintenance", "upgrade", "init"):
                    continue
                if discard_frozen and data.get("frozen", False):
                    # node frozen
                    continue
                instance = self.get_service_instance(svc.svcname, nodename)
                if instance is None:
                    continue
                if discard_start_failed and instance["monitor"]["status"] == "start failed":
                    continue
                if "avail" not in instance:
                    # deleting
                    continue
                if discard_frozen and instance.frozen:
                    continue
                if discard_unprovisioned and instance.provisioned is False:
                    continue
                if discard_constraints_violation and not instance.get("constraints", True):
                    continue
                if discard_overloaded and self.node_overloaded(nodename):
                    continue
                candidates.append(nodename)
        return candidates

    def placement_ranks(self, svc, candidates=None):
        if candidates is None:
            candidates = self.placement_candidates(svc)
        if svc.placement == "nodes order":
            return self.placement_ranks_nodes_order(svc, candidates)
        elif svc.placement == "shift":
            return self.placement_ranks_shift(svc, candidates)
        elif svc.placement == "spread":
            return self.placement_ranks_spread(svc, candidates)
        elif svc.placement == "score":
            return self.placement_ranks_score(svc, candidates)
        elif svc.placement == "load avg":
            return self.placement_ranks_load_avg(svc, candidates)
        else:
            return [rcEnv.nodename]

    def duplog(self, lvl, msg, **kwargs):
        sig = str(kwargs.items())
        if sig is None:
            return
        if sig in self.duplog_data and msg == self.duplog_data[sig]:
            return
        self.duplog_data[sig] = msg
        if lvl == "info":
            fn = self.log.info
        elif lvl == "warning":
            fn = self.log.warning
        elif lvl == "error":
            fn = self.log.error
        else:
            return
        fn(msg, kwargs)

    def placement_leader(self, svc, candidates=None, silent=False):
        if candidates is None:
            candidates = self.placement_candidates(svc)
        if len(candidates) == 0:
            if not silent:
                self.duplog("info", "placement constraints prevent us from starting "
                            "service %(svcname)s on any node", svcname=svc.svcname)
            return False
        if rcEnv.nodename not in candidates:
            if not silent:
                self.duplog("info", "placement constraints prevent us from starting "
                            "service %(svcname)s on this node", svcname=svc.svcname)
            return False
        if len(candidates) == 1:
            if not silent:
                self.duplog("info", "we have the greatest placement priority for "
                            "service %(svcname)s (alone)", svcname=svc.svcname)
            return True

        ranks = self.placement_ranks(svc, candidates=candidates)
        if ranks == []:
            return False
        elif svc.topology == "failover":
            if rcEnv.nodename == ranks[0]:
                if not silent:
                    self.duplog("info", "we have the highest '%(placement)s' "
                                "placement priority for failover service %(svcname)s",
                                placement=svc.placement, svcname=svc.svcname)
                return True
            else:
                if not silent:
                    self.duplog("info", "node %(nodename)s is alive and has a higher "
                                  "'%(placement)s' placement priority for "
                                  "failover service %(svcname)s",
                                  nodename=ranks[0], placement=svc.placement,
                                  svcname=svc.svcname)
                return False
        elif svc.topology == "flex":
            index = ranks.index(rcEnv.nodename) + 1
            if not silent:
                self.duplog("info", "we have the %(idx)d/%(mini)d '%(placement)s'"
                            " placement priority for flex service %(svcname)s",
                            idx=index, mini=svc.flex_min_nodes,
                            placement=svc.placement, svcname=svc.svcname)
            if index <= svc.flex_min_nodes:
                return True
            else:
                return False

    def placement_ranks_spread(self, svc, candidates, silent=False):
        """
        hash together each candidate nodename+svcname, and sort the resulting
        list.
        """
        def fn(s):
            h = hashlib.md5()
            h.update(s.encode())
            return h.digest()
        return [nodename for nodename in sorted(candidates, key=lambda x: fn(svc.svcname+x))]

    def placement_ranks_score(self, svc, candidates, silent=False):
        data = []
        with CLUSTER_DATA_LOCK:
            for nodename in CLUSTER_DATA:
                if nodename not in candidates:
                    continue
                try:
                    load = CLUSTER_DATA[nodename]["stats"]["score"]
                except KeyError:
                    pass
                data.append((nodename, load))
        return [nodename for nodename, _ in sorted(data, key=lambda x: -x[1])]

    def placement_ranks_load_avg(self, svc, candidates, silent=False):
        data = []
        with CLUSTER_DATA_LOCK:
            for nodename in CLUSTER_DATA:
                if nodename not in candidates:
                    continue
                try:
                    load = CLUSTER_DATA[nodename]["stats"]["load_15m"]
                except KeyError:
                    try:
                        load = CLUSTER_DATA[nodename]["load"]["15m"]
                    except KeyError:
                        continue
                data.append((nodename, load))
        return [nodename for nodename, _ in sorted(data, key=lambda x: x[1])]

    def placement_ranks_nodes_order(self, svc, candidates, silent=False):
        return [nodename for nodename in svc.ordered_peers if nodename in candidates]

    def placement_ranks_shift(self, svc, candidates, silent=False):
        ranks = self.placement_ranks_nodes_order(svc, candidates, silent=silent) * 2
        n_candidates = len(candidates)
        if n_candidates == 0:
            idx = 0
        else:
            idx = svc.slave_num % n_candidates
        return ranks[idx:idx+n_candidates]

    def get_oldest_gen(self, nodename=None):
        """
        Get oldest generation of the local dataset on peers.
        """
        if nodename is None:
             gens = LOCAL_GEN.values()
             if len(gens) == 0:
                 return 0, 0
             gen = min(gens)
             num = len(gens)
             ##self.log.info("oldest gen is %d amongst %d nodes", gen, num)
        else:
             if nodename not in LOCAL_GEN:
                 return 0, 0
             gen = LOCAL_GEN.get(nodename, 0)
             num = 1
             ##self.log.info("gen on node %s is %d", nodename, gen)
        return gen, num

    def purge_log(self):
        oldest, num = self.get_oldest_gen()
        if num == 0:
            # alone, truncate the log, we'll do a full
            to_remove = [gen for gen in GEN_DIFF]
        else:
            to_remove = [gen for gen in GEN_DIFF if gen < oldest]
        for gen in to_remove:
            ##self.log.info("purge gen %d", gen)
            del GEN_DIFF[gen]

    @staticmethod
    def mon_changed():
        return MON_CHANGED != []

    @staticmethod
    def unset_mon_changed():
        global MON_CHANGED
        MON_CHANGED = []

    @staticmethod
    def get_gen(inc=False):
        global GEN
        if inc:
           GEN += 1
        gen = {rcEnv.nodename: GEN}
        gen.update(REMOTE_GEN)
        return gen

    def node_overloaded(self, nodename=None):
        if nodename is None:
            nodename = rcEnv.nodename
        node_data = CLUSTER_DATA.get(nodename)
        if node_data is None:
            return False
        for key in ("mem", "swap"):
            limit = node_data.get("min_avail_"+key, 0)
            val = node_data.get("stats", {}).get(key+"_avail", 0)
            if val < limit:
                return True
        return False
 0707010005c8de000081b40000000000000000000000015ab21754000008ad000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/osvcd_winservice.py    """

 Author: Alex Baker
 Date: 7th July 2008
 Description : Simple python program to generate wrap as a service based on example on the web, see link below.

 http://essiene.blogspot.com/2005/04/python-windows-services.html

 Usage : python aservice.py install
 Usage : python aservice.py start
 Usage : python aservice.py stop
 Usage : python aservice.py remove

 C:\>python aservice.py  --username <username> --password <PASSWORD> --startup auto install

"""

import win32service
import win32serviceutil
import win32api
import win32con
import win32event
import win32evtlogutil
import os
import servicemanager
import sys

from osvcd import Daemon

class OsvcAgent(win32serviceutil.ServiceFramework):

    _svc_name_ = "OsvcAgent"
    _svc_display_name_ = "OpenSVC agent"
    _svc_description_ = "Orchestration, HA, inventoring, monitoring, config mgmt"

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        sys.stop_agent = True
        win32event.SetEvent(self.hWaitStop)

    def SvcDoRun(self):
        servicemanager.LogMsg(
            servicemanager.EVENTLOG_INFORMATION_TYPE,
            servicemanager.PYS_SERVICE_STARTED,
            (self._svc_name_, '')
        )
        os.chdir(os.path.join(os.path.dirname(__file__), ".."))
        daemon = Daemon()

        while True:
            # Wait for service stop signal, if I timeout, loop again
            rc = win32event.WaitForSingleObject(self.hWaitStop, 5000)
            # Check to see if self.hWaitStop happened
            if rc == win32event.WAIT_OBJECT_0:
                # Stop signal encountered
                servicemanager.LogInfoMsg("%s - STOPPED"%self._svc_name_)
                daemon.stop()
                break
            else:
                #servicemanager.LogInfoMsg("%s - ALIVE"%self._svc_name_)
                daemon.loop()
                pass

def ctrlHandler(ctrlType):
    return True

if __name__ == '__main__':
    win32api.SetConsoleCtrlHandler(ctrlHandler, True)
    win32serviceutil.HandleCommandLine(OsvcAgent)

   0707010005c8e0000081b40000000000000000000000015ab2175400000522000000660000000000000000000000000000003200000000root/usr/share/opensvc/lib/provContainerAmazon.py import provisioning

class Prov(provisioning.Prov):
    def provisioner(self):
        prereq = True
        if self.r.image_id is None:
            self.r.log.error("the image keyword is mandatory for the provision action")
            prereq &= False
        if self.r.size_id is None:
            self.r.log.error("the size keyword is mandatory for the provision action")
            prereq &= False
        if self.r.subnet_name is None:
            self.r.log.error("the subnet keyword is mandatory for the provision action")
            prereq &= False
        if self.r.key_name is None:
            self.r.log.error("the key_name keyword is mandatory for the provision action")
            prereq &= False
        if not prereq:
            raise ex.excError()

        c = self.r.get_cloud()
        image = self.r.get_image(self.r.image_id)
        size = self.r.get_size()
        subnet = self.r.get_subnet()
        self.r.log.info("create instance %s, size %s, image %s, key %s, subnet %s"%(self.r.name, size.name, image.name, self.r.key_name, subnet.name))
        c.driver.create_node(name=self.r.name, size=size, image=image, ex_keyname=self.r.key_name, ex_subnet=subnet)
        self.r.log.info("wait for container up status")
        self.r.wait_for_fn(self.r.is_up, self.r.startup_timeout, 5)

  0707010005c8e1000081b40000000000000000000000015ab217540000010b000000660000000000000000000000000000003200000000root/usr/share/opensvc/lib/provContainerDocker.py import provisioning

class Prov(provisioning.Prov):
    def is_provisioned(self):
        return True

    def start(self):
        self.r._start()
        self.r.status(resfresh=True)

    def stop(self):
        self.r._stop()
        self.r.status(resfresh=True)

 0707010005c8e2000081b40000000000000000000000015ab2175400000470000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/provContainerLxd.py    import os
import provisioning
import rcExceptions as ex
from subprocess import PIPE

class Prov(provisioning.Prov):
    def is_provisioned(self):
        return self.r.has_it()

    def start(self):
        """
        Replace the container.lxd start method, to bypass the initial
        lxc import, as it can't suceed on nodes where the container
        storage is not yet available.
        """
        pass

    def provisioner(self):
        if self.r.has_it():
            return
        image = self.r.conf_get("launch_image")
        try:
            options = self.r.conf_get("launch_options")
        except ex.OptNotFound as exc:
            options = exc.default
        cmd = ["/usr/bin/lxc", "launch", image] + options + [self.r.name]
        ret, out, err = self.r.vcall(cmd, stdin=PIPE)
        if ret != 0:
            raise ex.excError
        self.r.wait_for_fn(self.r.is_up, self.r.startup_timeout, 2)
        self.r.can_rollback = True

    def unprovisioner(self):
        cmd = ["/usr/bin/lxc", "delete", self.r.name]
        ret, out, err = self.r.vcall(cmd)
        if ret != 0:
            raise ex.excError

0707010005c8e3000081b40000000000000000000000015ab217540000022b000000660000000000000000000000000000003500000000root/usr/share/opensvc/lib/provContainerOpenstack.py  import provisioning

class Prov(provisioning.Prov):
    def provision(self):
        c = self.r.get_cloud()
        image = self.r.get_template()
        size = self.r.get_size()
        self.r.log.info("create instance %s, size %s, image %s, key %s"%(self.r.name, size.name, image.name, self.r.key_name))
        c.driver.create_node(name=self.r.name, size=size, image=image, ex_keyname=self.r.key_name, ex_shared_ip_group_id=self.r.shared_ip_group)
        #self.r.wait_for_startup()
        self.r.wait_for_fn(self.r.is_up, self.r.startup_timeout, 5)

 0707010005c8e4000081b40000000000000000000000015ab21754000001e8000000660000000000000000000000000000003200000000root/usr/share/opensvc/lib/provContainerVcloud.py import provisioning

class Prov(provisioning.Prov):

    def provision(self):
        c = self.r.get_cloud()
        image = self.r.get_template()
        size = self.r.get_size()
        self.r.log.info("create instance %s, size %s, image %s, key %s"%(self.r.name, size.name, image.name, self.r.key_name))
        c.driver.create_node(name=self.r.name, size=size, image=image, ex_key_name=self.r.key_name, ex_shared_ip_group_id=self.r.shared_ip_group)
        self.r.wait_for_startup()

0707010005c8e5000081b40000000000000000000000015ab21754000038eb000000660000000000000000000000000000003000000000root/usr/share/opensvc/lib/provContainerZone.py   import os
import resContainerZone
import lock
import provisioning
from rcZfs import Dataset
import rcZone
import rcExceptions as ex
from rcGlobalEnv import rcEnv
from rcUtilitiesSunOS import get_solaris_version
from rcUtilities import justcall
import shutil


SYSIDCFG="/etc/sysidcfg"

class Prov(provisioning.Prov):
    def __init__(self, r):
        """
        """
        provisioning.Prov.__init__(self, r)
        self.log = self.r.log
        self.section = r.svc.config.defaults()

        try:
            self.container_origin = r.conf_get("container_origin")
        except ex.OptNotFound as exc:
            self.container_origin = exc.default

        if 'snapof' in self.section:
            self.snapof = self.section['snapof']
        else:
            self.snapof = None

        if 'snap' in self.section:
            self.clone = self.section['snap']
        else:
            self.clone = "rpool/zones/" + r.name

        if 'zonepath' in self.section:
            self.zonepath = self.section['zonepath']
        else:
            self.zonepath = None

    def stop(self):
        self.r._stop()

    def start(self):
        pass

    def sysid_network(self):
        """
         network_interface=l226z1 {primary
          hostname=zone1-32
          ip_address=172.30.5.232
          netmask=255.255.255.0
          protocol_ipv6=no
          default_route=172.30.5.1}
        """
        cf = os.path.join(rcEnv.paths.pathetc, self.r.svc.svcname+'.conf')
        s = ""

        for r in self.r.svc.get_resources(["ip"]):
            # Add mandatory tags for sol11 zones
            r.tags.add("noaction")
            r.tags.add("noalias")
            r.tags.add("exclusive")
            r.tags.remove("preboot")
            r.tags.remove("postboot")

            # Add nonrouted tag if no gateway provisioning keyword is passed
            if not self.r.svc.config.has_option(r.rid, "gateway"):
                self.r.tags.add("nonrouted")

            if not self.r.svc.config.has_option(r.rid, "gateway"):
                continue
            default_route = self.r.svc.config.get(r.rid, "gateway")

            if not self.r.svc.config.has_option(r.rid, "netmask"):
                continue
            netmask = self.r.svc.config.get(r.rid, "netmask")

            if s == "":
                s += "network_interface=%s {primary\n"%r.ipdev
                s += " hostname=%s\n"%r.ipname
                s += " ip_address=%s\n"%r.addr
                s += " netmask=%s\n"%netmask
                s += " protocol_ipv6=no\n"
                s += " default_route=%s}\n"%default_route

            # save new service env file
            self.r.svc._set_multi(r.rid, "tags", ' '.join(r.tags))

        return s

    def get_tz(self):
        if "TZ" not in os.environ:
            return "MET"
        tz = os.environ["TZ"]
        tzp = os.path.join(os.sep, "etc", tz)
        if os.path.exists(tzp) and self.osver >= 11:
            p = os.path.realpath(tzp)
            l = p.split('zoneinfo/')
            if len(l) != 2:
                return "MET"
            return l[-1]
        else:
            return tz

    def get_ns(self):
        "return (domain, nameservers) detected from resolv.conf"
        p = os.path.join(os.sep, 'etc', 'resolv.conf')
        domain = None
        search = []
        nameservers = []
        with open(p) as f:
            for line in f.readlines():
                if line.strip().startswith('search'):
                    l = line.split()
                    if len(l) > 1:
                        search = l[1:]
                if line.strip().startswith('domain'):
                    l = line.split()
                    if len(l) > 1:
                        domain = l[1]
                if line.strip().startswith('nameserver'):
                    l = line.split()
                    if len(l) > 1 and l[1] not in nameservers:
                        nameservers.append(l[1])
        return (domain, nameservers, search)

    def create_sysidcfg(self, zone=None):
        self.r.log.info("creating zone sysidcfg file")
        if self.osver >= 11.0:
            self._create_sysidcfg_11(zone)
        else:
            self._create_sysidcfg_10(zone)

    def _create_sysidcfg_11(self, zone=None):
        try:
            domain, nameservers, search = self.get_ns()
            if domain is None and len(search) > 0:
                domain = search[0]
            if domain is None or len(nameservers) == 0:
                name_service="name_service=none"
            else:
                name_service = "name_service=DNS {domain_name=%s name_server=%s search=%s}\n" % (
                  domain,
                  ",".join(nameservers),
                  ",".join(search)
                )

            sysidcfg_dir = os.path.join(self.r.var_d)
            sysidcfg_filename = os.path.join(sysidcfg_dir, 'sysidcfg')
            contents = ""
            contents += "keyboard=US-English\n"
            contents += "system_locale=C\n"
            contents += "timezone=%s\n"%self.get_tz()
            contents += "terminal=vt100\n"
            contents += "timeserver=localhost\n"
            contents += self.sysid_network()
            contents += "root_password=NP\n"
            contents += "security_policy=NONE\n"
            contents += name_service

            try:
                os.makedirs(sysidcfg_dir)
            except:
                pass
            with open(sysidcfg_filename, "w") as sysidcfg_file:
                sysidcfg_file.write(contents)
            os.chdir(sysidcfg_dir)
            self.zonecfg_xml = os.path.join(sysidcfg_dir, "sc_profile.xml")
            try:
                os.unlink(self.zonecfg_xml)
            except:
                pass
            cmd = ['/usr/sbin/js2ai', '-s']
            out, err, ret = justcall(cmd)
            if not os.path.exists(self.zonecfg_xml):
                raise ex.excError("js2ai conversion error")
        except Exception as e:
            self.r.svc.save_exc()
            raise ex.excError("exception from %s: %s during create_sysidcfg file" % (e.__class__.__name__, e.__str__()))

    def _create_sysidcfg_10(self, zone=None):
        try:
            name_service = "name_service=NONE\n"

            sysidcfg_filename = zone.get_zonepath() + "/root" + SYSIDCFG
            sysidcfg_file = open(sysidcfg_filename, "w" )
            contents = ""
            contents += "system_locale=C\n"
            contents += "timezone=MET\n"
            contents += "terminal=vt100\n"
            contents += "timeserver=localhost\n"
            contents += "security_policy=NONE\n"
            contents += "root_password=NP\n"
            contents += "nfs4_domain=dynamic\n"
            contents += "network_interface=NONE {hostname=%(zonename)s}\n" % {"zonename":zone.name}
            contents += name_service

            sysidcfg_file.write(contents)
            sysidcfg_file.close()
        except Exception as exc:
            raise ex.excError("exception from %s: %s during create_sysidcfg file" % (exc.__class__.__name__, exc.__str__()))

    def test_net_interface(self, intf):
        cmd = ['dladm', 'show-link', intf]
        out, err, ret = justcall(cmd)
        if ret == 0:
            return True
        return False

    def zone_configure_net(self, zone=None):
        if zone is None:
            zone = self.r
        cmds = []
        for r in self.r.svc.get_resources(["ip"]):
            if not self.test_net_interface(r.ipdev):
                raise ex.excError("Missing interface: %s" % r.ipdev)
            cmds.append("add net ; set physical=%s ; end" % r.ipdev)
        for cmd in cmds:
            zone.zonecfg([cmd])

    def zone_configure(self, zone=None):
        """
            configure zone, if zone is None, configure self.r
        """
        if zone is None:
            zone = self.r

        if self.osver >= 11.0 and self.container_origin:
            cmd = "create -t " + self.container_origin
        else:
            cmd = "create"

        cmd += "; set zonepath=" + zone.get_zonepath()

        if zone.state is None:
            zone.zonecfg([cmd])
            if zone.state != "configured":
                raise ex.excError("zone %s is not configured" % zone.name)

        if self.osver >= 11.0:
            try:
                self.zone_configure_net(zone)
            except:
                zone.zonecfg(["delete", "-F"])
                raise

    def create_zone2clone(self):
        if os.path.exists(self.r.zonepath):
            try:
                os.chmod(self.r.zonepath, 0o0700)
            except:
                pass
        if self.osver >= 11.0:
            self._create_zone2clone_11()
        else:
            self._create_zone2clone_10()

    def _create_zone2clone_11(self):
        zonename = self.container_origin
        zone2clone = resContainerZone.Zone(rid="container#skelzone", name=zonename)
        zone2clone.log = self.r.log
        if zone2clone.state == "installed":
            return
        self.zone_configure(zone=zone2clone)
        if zone2clone.state != "configured":
            raise(ex.excError("zone %s is not configured" % (zonename)))
        self.create_sysidcfg(zone2clone)
        #zone2clone.zoneadm("clone", ['-c', self.zonecfg_xml, self.container_origin])
        zone2clone.zoneadm("install")
        if zone2clone.state != "installed":
            raise(ex.excError("zone %s is not installed" % (zonename)))
        brand = zone2clone.brand
        if brand == "native":
            zone2clone.boot_and_wait_reboot()
        elif brand == "ipkg":
            zone2clone.boot()
        else:
            raise(ex.excError("zone brand: %s not yet implemented" % (brand)))
        zone2clone.wait_multi_user()
        zone2clone.stop()
        zone2clone.zone_refresh()
        if zone2clone.state != "installed":
            raise(ex.excError("zone %s is not installed" % (zonename)))

    def _create_zone2clone_10(self):
        """verify if self.container_origin zone is installed
        else configure container_origin if required
        then install container_origin if required
        """
        zonename = self.container_origin
        zone2clone = resContainerZone.Zone(rid="container#skelzone", name=zonename)
        zone2clone.log = self.r.log
        if zone2clone.state == "installed":
            return
        self.zone_configure(zone=zone2clone)
        if zone2clone.state != "configured":
            raise(ex.excError("zone %s is not configured" % (zonename)))
        zone2clone.zoneadm("install")
        if zone2clone.state != "installed":
            raise(ex.excError("zone %s is not installed" % (zonename)))
        self.create_sysidcfg(zone2clone)
        brand = zone2clone.brand
        if brand == "native":
            zone2clone.boot_and_wait_reboot()
        elif brand == "ipkg":
            zone2clone.boot()
        else:
            raise(ex.excError("zone brand: %s not yet implemented" % (brand)))
        zone2clone.wait_multi_user()
        zone2clone.stop()
        zone2clone.zone_refresh()
        if zone2clone.state != "installed":
            raise(ex.excError("zone %s is not installed" % (zonename)))

    def create_cloned_zone(self):
        zone = self.r
        if zone.state == "running":
            self.log.info("zone %s already running"%zone.name)
            return
        if zone.state == "configured":
            if self.osver >= 11.0:
                self._create_cloned_zone_11(zone)
            else:
                self._create_cloned_zone_10(zone)
        if zone.state != "installed":
            raise(ex.excError("zone %s is not installed" % (zone.name)))

    def _create_cloned_zone_11(self, zone):
        zone.zoneadm("clone", ['-c', self.zonecfg_xml, self.container_origin])

    def _create_cloned_zone_10(self, zone):
        zone.zoneadm("clone", [self.container_origin])

    def create_zonepath(self):
        """create zonepath dataset from clone of snapshot of self.snapof
        snapshot for self.snapof will be created
        then cloned to self.clone
        """
        zonename = self.r.name
        source_ds = Dataset(self.snapof)
        if source_ds.exists(type="filesystem") is False:
            raise(ex.excError("source dataset doesn't exist " + self.snapof))
        snapshot = source_ds.snapshot(zonename)
        snapshot.clone(self.clone, ['-o', 'mountpoint=' + self.r.zonepath])

    def provisioner(self, need_boot=True):
        """provision zone
        - configure zone
        - if snapof and zone brand is native
           then create zonepath from snapshot of snapof
           then attach zone
        - if snapof and zone brand is ipkg
           then try to detect zone associated with snapof
           then define container_origin
        - if container_origin
           then clone  container_origin
        - create sysidcfg
        - if need_boot boot and wait multiuser
        """
        self.osver = get_solaris_version()
        self.zone_configure()

        if self.osver >= 11:
            self.create_sysidcfg(self.r)
        else:
            if self.snapof is not None and self.r.brand == 'native':
                self.create_zonepath()
                self.r.zoneadm("attach", ["-F"])
            elif self.snapof is not None and self.r.brand == 'ipkg':
                zones = rcZone.Zones()
                src_dataset = Dataset(self.snapof)
                zonepath = src_dataset.getprop('mountpoint')
                self.container_origin = zones.zonename_from_zonepath(zonepath).zonename
                self.log.info("source zone is %s (detected from snapof %s)" % (self.container_origin, self.snapof))

        if self.container_origin is not None:
            lockname='create_zone2clone-' + self.container_origin
            lockfile = os.path.join(rcEnv.paths.pathlock, lockname)
            self.log.info("wait get lock %s"%(lockname))
            try:
                lockfd = lock.lock(timeout=1200, delay=5, lockfile=lockfile)
            except:
                raise(ex.excError("failure in get lock %s"%(lockname)))
            try:
                self.create_zone2clone()
            except:
                lock.unlock(lockfd)
                raise
            lock.unlock(lockfd)
            self.create_cloned_zone()

        if self.osver < 11:
            self.create_sysidcfg(self.r)

        if need_boot is True:
            self.r.boot()
            self.r.wait_multi_user()

        self.r.log.info("provisioned")
        return True
 0707010005c8e6000081b40000000000000000000000015ab21754000006e8000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/provDiskAmazon.py  import provisioning
import rcExceptions as ex

class Prov(provisioning.Prov):
    def __init__(self, r):
        provisioning.Prov.__init__(self, r)
        self.volumes_done = []

    def provisioner(self):
        for volume in self.r.volumes:
            self._provisioner(volume)
        volumes = ' '.join(self.volumes_done)
        self.r.svc.config.set(self.r.rid, "volumes", volumes)
        self.r.svc.write_config()
        self.r.volumes = self.volumes_done
        self.r.log.info("provisioned")
        self.r.start()
        self.r.svc.node.unset_lazy("devtree")

    def _provisioner(self, volume):
        if not volume.startswith("<") and not volume.endswith(">"):
            self.r.log.info("volume %s already provisioned" % volume)
            self.volumes_done.append(volume)
            return

        s = volume.strip("<>")
        v = s.split(",")
        kwargs = {}
        for e in v:
            try:
                key, val = e.split("=")
            except:
                raise ex.excError("format error: %s. expected key=value." % e)
            kwargs[key] = val
        cmd = ["ec2", "create-volume"]
        if "size" in kwargs:
            cmd += ["--size", kwargs["size"]]
        if "iops" in kwargs:
            cmd += ["--iops", kwargs["iops"]]
        if "availability-zone" in kwargs:
            cmd += ["--availability-zone", kwargs["availability-zone"]]
        else:
            node = self.r.get_instance_data()
            availability_zone = node["Placement"]["AvailabilityZone"]
            cmd += ["--availability-zone", availability_zone]
        data = self.r.aws(cmd)
        self.r.wait_avail(data["VolumeId"])
        self.volumes_done.append(data["VolumeId"])
        self.r.svc.node.unset_lazy("devtree")


0707010005c8e7000081b40000000000000000000000015ab2175400000854000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/provDiskDisk.py    import provisioning

class Prov(provisioning.Prov):

    def provision(self):
        if self.r.disk_id is not None:
            self.r.log.info("skip provision: 'disk_id' is already set")
            return
        try:
            size = self.r.svc.conf_get(self.r.rid, "size")
        except:
            raise ex.excError("disk %s: missing the 'size' provisioning parameter" % self.r.rid)
        try:
            slo = self.r.svc.conf_get(self.r.rid, "slo")
        except:
            slo = None

        handler = "/services/self.r/disks"
        data = {
            "action": "provision",
            "size": size,
            "array_name": self.r.array_name,
            "diskgroup": self.r.diskgroup,
        }
        if slo is not None:
            data["slo"] = slo
        results = self.r.svc.collector_rest_put(handler, data)
        if "error" in results:
            raise ex.excError(results["error"])
        self.r.log.info("disk provision request sent to the collector (id %d). "
                      "waiting for completion." % results["results_id"])

        results = self.r.wait_results(results)
        self.r.disk_id = results["outputs"]["add disk"][0]["disk_id"]
        self.r.set_label()
        self.r.svc.config.set(self.r.rid, "disk_id", self.r.disk_id)
        self.r.svc.write_config()
        self.r.log.info("disk %s provisioned" % self.r.disk_id)
        self.r.svc.node.unset_lazy("devtree")

    def unprovision(self):
        handler = "/services/self.r/disks"
        data = {
            "action": "unprovision",
            "disk_id": self.r.disk_id,
        }
        results = self.r.svc.collector_rest_put(handler, data)
        if "error" in results:
            raise ex.excError(results["error"])
        self.r.log.info("disk unprovision request sent to the collector (id %d). "
                      "waiting for completion." % results["results_id"])
        results = self.r.wait_results(results)
        self.r.svc.config.set(self.r.rid, "disk_id", "")
        self.r.svc.write_config()
        self.r.log.info("unprovisioned")
        self.r.svc.node.unset_lazy("devtree")

0707010005c8e8000081b40000000000000000000000015ab21754000009a5000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/provDiskGce.py import provisioning
import rcExceptions as ex
from converters import convert_size

class Prov(provisioning.Prov):
    def __init__(self, r):
        provisioning.Prov.__init__(self, r)

    def provisioner(self):
        for name in self.r.names:
            self._provisioner(name)
        self.r.log.info("provisioned")
        self.r.get_disks(refresh=True)
        self.r.start()
        self.r.svc.node.unset_lazy("devtree")

    def _provisioner(self, name):
        disk_names = self.r.get_disk_names()
        if name in disk_names:
            self.r.log.info("gce disk name %s already provisioned" % name)
            return

        try:
            size = self.r.svc.conf_get(self.r.rid, "size")
        except:
            raise ex.excError("gce disk name %s in %s: missing the 'size' parameter" % (name, self.r.rid))
        size = str(convert_size(size, _to="MB"))+'MB'

        cmd = ["gcloud", "compute", "disks", "create", "-q",
               name,
               "--size", size,
               "--zone", self.r.gce_zone]

        try:
            description = self.r.svc.conf_get(self.r.rid, "description")
            cmd += ["--description", description]
        except:
            pass

        try:
            image = self.r.svc.conf_get(self.r.rid, "image")
            cmd += ["--image", image]
        except:
            pass

        try:
            source_snapshot = self.r.svc.conf_get(self.r.rid, "source_snapshot")
            cmd += ["--source-snapshot", source_snapshot]
        except:
            pass

        try:
            image_project = self.r.svc.conf_get(self.r.rid, "image_project")
            cmd += ["--image-project", image_project]
        except:
            pass

        try:
            disk_type = self.r.svc.conf_get(self.r.rid, "disk_type")
            cmd += ["--type", disk_type]
        except:
            pass

        self.r.vcall(cmd)


    def unprovisioner(self):
        self.r.stop()
        for name in self.r.names:
            self._unprovisioner(name)
        self.r.log.info("unprovisioned")
        self.r.svc.node.unset_lazy("devtree")

    def _unprovisioner(self, name):
        disk_names = self.r.get_disk_names()
        if name not in disk_names:
            self.r.log.info("gce disk name %s already unprovisioned" % name)
            return

        cmd = ["gcloud", "compute", "disks", "delete", "-q", name,
               "--zone", self.r.gce_zone]

        self.r.vcall(cmd)


   0707010005c8e9000081b40000000000000000000000015ab21754000005a5000000660000000000000000000000000000003100000000root/usr/share/opensvc/lib/provDiskLoopDarwin.py  import provisioning
import os
import rcExceptions as ex
from converters import convert_size

class Prov(provisioning.Prov):
    def __init__(self, r):
        provisioning.Prov.__init__(self, r)

    def is_provisioned(self):
        try:
            return os.path.exists(self.r.loopFile)
        except Exception:
            return

    def unprovisioner(self):
        try:
            self.path = self.r.loopFile
        except Exception as e:
            raise ex.excError(str(e))

        if not self.is_provisioned():
            return

        self.r.log.info("unlink %s" % self.path)
        os.unlink(self.path)
        self.r.svc.node.unset_lazy("devtree")

    def provisioner(self):
        try:
            self.path = self.r.loopFile
            self.size = self.r.svc.conf_get(self.r.rid, "size")
        except Exception as e:
            raise ex.excError(str(e))

        d = os.path.dirname(self.path)
        try:
            if not os.path.exists(d):
                self.r.log.info("create directory %s"%d)
                os.makedirs(d)
            with open(self.path, 'w') as f:
                self.r.log.info("create file %s, size %s"%(self.path, self.size))
                f.seek(convert_size(self.size, _to='b', _round=512)-1)
                f.write('\0')
        except Exception as e:
            raise ex.excError("failed to create %s: %s"% (self.path, str(e)))
        self.r.svc.node.unset_lazy("devtree")
   0707010005c8ea000081b40000000000000000000000015ab21754000005a5000000660000000000000000000000000000003000000000root/usr/share/opensvc/lib/provDiskLoopLinux.py   import provisioning
import os
import rcExceptions as ex
from converters import convert_size

class Prov(provisioning.Prov):
    def __init__(self, r):
        provisioning.Prov.__init__(self, r)

    def is_provisioned(self):
        try:
            return os.path.exists(self.r.loopFile)
        except Exception:
            return

    def unprovisioner(self):
        try:
            self.path = self.r.loopFile
        except Exception as e:
            raise ex.excError(str(e))

        if not self.is_provisioned():
            return

        self.r.log.info("unlink %s" % self.path)
        os.unlink(self.path)
        self.r.svc.node.unset_lazy("devtree")

    def provisioner(self):
        try:
            self.path = self.r.loopFile
            self.size = self.r.svc.conf_get(self.r.rid, "size")
        except Exception as e:
            raise ex.excError(str(e))

        d = os.path.dirname(self.path)
        try:
            if not os.path.exists(d):
                self.r.log.info("create directory %s"%d)
                os.makedirs(d)
            with open(self.path, 'w') as f:
                self.r.log.info("create file %s, size %s"%(self.path, self.size))
                f.seek(convert_size(self.size, _to='b', _round=512)-1)
                f.write('\0')
        except Exception as e:
            raise ex.excError("failed to create %s: %s"% (self.path, str(e)))
        self.r.svc.node.unset_lazy("devtree")
   0707010005c8eb000081b40000000000000000000000015ab2175400000551000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/provDiskLvHP-UX.py import provisioning
from rcUtilities import justcall, which
from converters import convert_size
from rcGlobalEnv import rcEnv
import os
import rcExceptions as ex
import time

class Prov(provisioning.Prov):
    def __init__(self, r):
        provisioning.Prov.__init__(self, r)

    def provisioner(self):
        if not which('vgdisplay'):
            self.r.log.error("vgdisplay command not found")
            raise ex.excError

        if not which('lvcreate'):
            self.r.log.error("lvcreate command not found")
            raise ex.excError

        try:
            self.size = self.r.svc.conf_get(self.r.rid, "size")
            self.size = convert_size(self.size, _to="m")
            self.vg = self.r.svc.conf_get(self.r.rid, "vg")
        except Exception as e:
            self.r.log.info("skip lv provisioning: %s" % str(e))
            return

        cmd = ['vgdisplay', self.vg]
        out, err, ret = justcall(cmd)
        if ret != 0:
            self.r.log.error("volume group %s does not exist"%self.vg)
            raise ex.excError

        dev = os.path.basename(self.r.device)

        # create the logical volume
        cmd = ['lvcreate', '-n', dev, '-L', str(self.size)+'M', self.vg]
        ret, out, err = self.r.vcall(cmd)
        if ret != 0:
            raise ex.excError

        self.r.svc.node.unset_lazy("devtree")


   0707010005c8ec000081b40000000000000000000000015ab21754000018e4000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/provDiskLvLinux.py import provisioning
from rcUtilities import justcall, which
from converters import convert_size
from rcUtilities import bdecode
from rcUtilitiesLinux import label_to_dev
from rcGlobalEnv import rcEnv
from subprocess import *
import os
import rcExceptions as ex
import time
import signal

def restore_signals():
    # from http://hg.python.org/cpython/rev/768722b2ae0a/
    signals = ('SIGPIPE', 'SIGXFZ', 'SIGXFSZ')
    for sig in signals:
        if hasattr(signal, sig):
           signal.signal(getattr(signal, sig), signal.SIG_DFL)

class Prov(provisioning.Prov):
    def __init__(self, r):
        provisioning.Prov.__init__(self, r)

    def get_dev(self):
        """
        Return the device path in the /dev/<vg>/<lv> format
        """
        if hasattr(self.r, "fullname"):
            # the resource is a lv
            return "/dev/" + self.r.fullname

        # the resource is a fs
        try:
            dev = self.r.conf_get("dev")
        except ex.RequiredOptNotFound:
            raise ex.excError

        if dev.startswith("LABEL=") or dev.startswith("UUID="):
            try:
                _dev = label_to_dev(dev, tree=self.r.svc.node.devtree)
            except ex.excError as exc:
                _dev = None
            if _dev is None:
                self.r.log.info("unable to find device identified by %s", dev)
                return
            dev = _dev

        try:
            vg = self.r.conf_get("vg")
        except ex.RequiredOptNotFound:
            raise ex.excError

        if dev.startswith('/dev/mapper/'):
            dev = dev.replace(vg.replace('-', '--')+'-', '')
            dev = dev.replace('--', '-')
            return "/dev/"+vg+"/"+os.path.basename(dev)
        if "/"+vg+"/" in dev:
            return dev
        self.r.log.error("unexpected dev %s format" % self.r.device)
        raise ex.excError

    def activate(self, dev):
        if not which('lvchange'):
            self.r.log.debug("lvchange command not found")
            return

        dev = self.get_dev()
        if dev is None:
            return
        cmd = ["lvchange", "-a", "y", dev]
        ret, out, err = self.r.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def unprovisioner(self):
        try:
            vg = self.r.conf_get("vg")
        except:
            self.r.log.debug("skip lv unprovision: no vg option")
            return

        if not which('lvdisplay'):
            self.r.log.debug("skip lv unprovision: lvdisplay command not found")
            return

        dev = self.get_dev()
        if dev is None:
            return
        cmd = ["lvdisplay", dev]
        out, err, ret = justcall(cmd)
        if ret != 0:
            self.r.log.debug("skip lv unprovision: %s is not a lv" % dev)
            return

        if not which('lvremove'):
            self.r.log.error("lvcreate command not found")
            raise ex.excError

        if which('wipefs') and os.path.exists(dev):
            self.r.vcall(["wipefs", "-a", dev])

        cmd = ["lvremove", "-f", dev]
        ret, out, err = self.r.vcall(cmd)
        if ret != 0:
            raise ex.excError
        self.r.svc.node.unset_lazy("devtree")

    def provisioner(self):
        if not which('vgdisplay'):
            self.r.log.error("vgdisplay command not found")
            raise ex.excError

        if not which('lvcreate'):
            self.r.log.error("lvcreate command not found")
            raise ex.excError

        if not which('lvdisplay'):
            self.r.log.error("lvdisplay command not found")
            raise ex.excError

        dev = self.get_dev()
        cmd = ["lvdisplay", dev]
        out, err, ret = justcall(cmd)
        if ret == 0:
            self.r.log.debug("skip lv provision: %s already exists" % dev)
            if "NOT available" in out:
                self.activate(dev)
            return

        try:
            self.size = self.r.conf_get("size")
            self.size = str(self.size).upper()
            if "%" not in self.size:
                size_parm = ["-L", str(convert_size(self.size, _to="m"))+'M']
            else:
                size_parm = ["-l", self.size]
            vg = self.r.conf_get("vg")
        except Exception as e:
            self.r.log.info("skip lv provisioning: %s" % str(e))
            return

        try:
            create_options = self.r.conf_get("create_options")
        except ex.OptNotFound:
            create_options = []
        cmd = ['vgdisplay', vg]
        out, err, ret = justcall(cmd)
        if ret != 0:
            self.r.log.error("volume group %s does not exist" % vg)
            raise ex.excError

        lvname = os.path.basename(dev)

        # create the logical volume
        cmd = ['lvcreate', '-n', lvname] + size_parm + create_options + [vg]
        _cmd = "yes | " + " ".join(cmd)
        self.r.log.info(_cmd)
        p1 = Popen(["yes"], stdout=PIPE, preexec_fn=restore_signals)
        p2 = Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=p1.stdout, close_fds=True)
        out, err = p2.communicate()
        out = bdecode(out)
        err = bdecode(err)
        if p2.returncode != 0:
            raise ex.excError(err)
        if hasattr(self.r, "fullname"):
            self.r.can_rollback = True
        if len(out) > 0:
            for line in out.splitlines():
                self.r.log.info(line)
        if len(err) > 0:
            for line in err.splitlines():
                if line.startswith("WARNING:"):
                    self.r.log.warning(line.replace("WARNING: ", ""))
                else:
                    self.r.log.error(err)

        # /dev/mapper/$vg-$lv and /dev/$vg/$lv creation is delayed ... refresh
        try:
            cmd = [rcEnv.syspaths.dmsetup, 'mknodes']
            p = Popen(cmd, stdout=PIPE, stderr=PIPE)
            p.communicate()
        except:
            # best effort
            pass
        mapname = "%s-%s" % (vg.replace('-','--'),
                             lvname.replace('-','--'))
        dev = '/dev/mapper/'+mapname

        for i in range(3, 0, -1):
            if os.path.exists(dev):
                break
            if i != 0:
                time.sleep(1)
        if i == 0:
            self.r.log.error("timed out waiting for %s to appear"%dev)
            raise ex.excError

        self.r.svc.node.unset_lazy("devtree")

0707010005c8ed000081b40000000000000000000000015ab2175400000d66000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/provDiskMdLinux.py import provisioning
import os
import rcExceptions as ex

from rcGlobalEnv import rcEnv
from rcUtilities import which
from converters import convert_size

class Prov(provisioning.Prov):
    def __init__(self, r):
        provisioning.Prov.__init__(self, r)

    def is_provisioned(self):
        if which("mdadm") is None:
            return
        return self.r.has_it()

    def provisioner(self):
        if which("mdadm") is None:
            raise ex.excError("mdadm is not installed")
        try:
            level = self.r.svc.conf_get(self.r.rid, "level")
        except:
            raise ex.excError("'level' provisioning parameter not set")
        try:
            devs = self.r.svc.conf_get(self.r.rid, "devs").split()
        except:
            raise ex.excError("'devs' provisioning parameter not set")
        if len(devs) == 0:
            raise ex.excError("at least 2 devices must be set in the 'devs' provisioning parameter")
        try:
            spares = self.r.svc.conf_get(self.r.rid, 'spares')
        except:
            spares = 0
        try:
            chunk = self.r.svc.conf_get(self.r.rid, 'chunk')
        except:
            chunk = None
        try:
            layout = self.r.svc.conf_get(self.r.rid, 'layout')
        except:
            layout = None

        # long md names cause a buffer overflow in mdadm
        name = "/dev/md/"+self.r.svc.svcname.split(".")[0]+"."+self.r.rid.replace("#", ".")
        cmd = [self.r.mdadm, '--create', name, '--force', '--quiet',
               '--metadata=default']
        cmd += ['-n', str(len(devs)-spares)]
        if level:
            cmd += ["-l", level]
        if spares:
            cmd += ["-x", str(spares)]
        if chunk:
            cmd += ["-c", str(convert_size(chunk, _to="k", _round=4))]
        if layout:
            cmd += ["-p", layout]
        cmd += devs
        ret, out, err = self.r.vcall(cmd)
        if ret != 0:
            raise ex.excError(err)
        self.r.can_rollback = True
        if len(out) > 0:
            self.r.log.info(out)
        if len(err) > 0:
            self.r.log.error(err)
        self.r.uuid = os.path.basename(name)
        uuid = self.get_real_uuid(name)
        self.r.uuid = uuid
        if self.r.shared:
            self.r.log.info("set %s.uuid = %s", self.r.rid, uuid)
            self.r.svc._set(self.r.rid, "uuid", uuid)
        else:
            self.r.log.info("set %s.uuid@%s = %s", self.r.rid, rcEnv.nodename, uuid)
            self.r.svc._set(self.r.rid, "uuid@"+rcEnv.nodename, uuid)
        self.r.svc.node.unset_lazy("devtree")

    def get_real_uuid(self, name):
        buff = self.r.detail()
        for line in buff.split("\n"):
            line = line.strip()
            if line.startswith("UUID :"):
                return line.split(" : ")[-1]
        raise ex.excError("unable to determine md uuid")

    def unprovisioner(self):
        if self.r.uuid == "" or self.r.uuid is None:
            return
        for dev in self.r.sub_devs():
            self.r.vcall([self.r.mdadm, "--brief", "--zero-superblock", dev])
        if self.r.shared:
            self.r.log.info("reset %s.uuid", self.r.rid)
            self.r.svc._set(self.r.rid, "uuid", "")
        else:
            self.r.log.info("reset %s.uuid@%s", self.r.rid, rcEnv.nodename)
            self.r.svc._set(self.r.rid, "uuid@"+rcEnv.nodename, "")
        self.r.svc.node.unset_lazy("devtree")

  0707010005c8ee000081b40000000000000000000000015ab2175400000434000000660000000000000000000000000000003100000000root/usr/share/opensvc/lib/provDiskRadosLinux.py  import provisioning
import rcExceptions as ex
from converters import convert_size

class Prov(provisioning.Prov):
    def __init__(self, r):
        provisioning.Prov.__init__(self, r)

    def provisioner(self):
        for image in self.r.images:
            self.provisioner_one(image)
        self.r.log.info("provisioned")
        self.r.start()
        self.r.svc.node.unset_lazy("devtree")

    def provisioner_one(self, image):
        if self.r.exists(image):
            self.r.log.info("%s already provisioned"%image)
            return
        size = self.r.conf_get('size')
        size = convert_size(size, _to="m")

        try:
            image_format = self.r.conf_get('image_format')
        except ex.OptNotFound as exc:
            image_format = exc.default

        cmd = self.r.rbd_rcmd() + ['create', '--size', str(size), image]
        if image_format:
            cmd += ["--image-format", str(image_format)]
        ret, out, err = self.r.vcall(cmd)
        if ret != 0:
            raise ex.excError
        self.r.svc.node.unset_lazy("devtree")

0707010005c8ef000081b40000000000000000000000015ab2175400000081000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/provDiskRaw.py import provisioning
import rcExceptions as ex

class Prov(provisioning.Prov):
    def is_provisioned(self):
        return True

   0707010005c8f0000081b40000000000000000000000015ab21754000006ee000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/provDiskVgHP-UX.py import provisioning
import os
import json
import rcExceptions as ex
from stat import *
from rcUtilities import justcall
import glob

class Prov(provisioning.Prov):
    def __init__(self, r):
        provisioning.Prov.__init__(self, r)
        self.pvs = r.svc.config.get(self.r.rid, 'pvs')
        self.pvs = self.pvs.split()
        try:
            self.options = r.svc.config.get(self.r.rid, 'options').split()
        except:
            self.options = []
        l = []
        for pv in self.pvs:
            l += glob.glob(pv)
        self.pvs = l

    def provisioner(self):
        if self.r.has_it():
            self.r.log.info("already provisioned")
            return

        err = False
        for i, pv in enumerate(self.pvs):
            if not os.path.exists(pv):
                self.r.log.error("pv %s does not exist"%pv)
                err |= True
            mode = os.stat(pv)[ST_MODE]
            if S_ISBLK(mode):
                continue
            else:
                self.r.log.error("pv %s is not a block device nor a loop file"%pv)
                err |= True
        if err:
            raise ex.excError

        for pv in self.pvs:
            pv = pv.replace('/disk/', '/rdisk/')
            cmd = ['pvcreate', '-f', pv]
            ret, out, err = self.r.vcall(cmd)
            if ret != 0:
                raise ex.excError

        pvs = []
        for pv in self.pvs:
            pvs.append(pv.replace('/rdisk/', '/disk/'))
        cmd = ['vgcreate']
        if len(self.options) > 0:
            cmd += self.options
        cmd += [self.r.name] + pvs
        ret, out, err = self.r.vcall(cmd)
        if ret != 0:
            raise ex.excError

        self.r.log.info("provisioned")
        self.r.svc.node.unset_lazy("devtree")
  0707010005c8f1000081b40000000000000000000000015ab2175400000e7c000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/provDiskVgLinux.py import os
import json
import glob
from stat import *

from rcGlobalEnv import rcEnv
import provisioning
import rcExceptions as ex
from rcUtilities import justcall

class Prov(provisioning.Prov):
    def __init__(self, r):
        provisioning.Prov.__init__(self, r)

    def is_provisioned(self):
        # don't trust cache for that
        self.r.clear_cache("vg.lvs")
        self.r.clear_cache("vg.lvs.attr")
        self.r.clear_cache("vg.tags")
        self.r.clear_cache("vg.pvs")
        return self.r.has_it()

    def unprovisioner(self):
        if not self.r.has_it():
            return
        cmd = ['vgremove', '-ff', self.r.name]
        ret, out, err = self.r.vcall(cmd)
        if ret != 0:
            raise ex.excError
        self.r.clear_cache("vg.lvs")
        self.r.clear_cache("vg.lvs.attr")
        self.r.clear_cache("vg.tags")
        self.r.clear_cache("vg.pvs")
        self.r.svc.node.unset_lazy("devtree")

    def has_pv(self, pv):
        cmd = [rcEnv.syspaths.pvscan, "--cache", pv]
        justcall(cmd)
        cmd = [rcEnv.syspaths.pvs, "-o", "vg_name", "--noheadings", pv]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return False
        out = out.strip()
        if out == self.r.name:
            self.r.log.info("pv %s is already a member of vg %s", pv, self.r.name)
            return True
        if out != "":
            raise ex.excError("pv %s in use by vg %s" % (pv, out))
        return True

    def provisioner(self):
        try:
            self.pvs = self.r.svc.conf_get(self.r.rid, "pvs")
        except ex.RequiredOptNotFound:
            raise ex.excError

        if self.pvs is None:
            # lazy reference not resolvable
            raise ex.excError("%s.pvs value is not valid" % self.r.rid)

        self.pvs = self.pvs.split()
        l = []
        for pv in self.pvs:
            _l = glob.glob(pv)
            self.r.log.info("expand %s to %s" % (pv, ', '.join(_l)))
            l += _l
        self.pvs = l

        pv_err = False
        for i, pv in enumerate(self.pvs):
            pv = os.path.realpath(pv)
            if not os.path.exists(pv):
                self.r.log.error("pv %s does not exist"%pv)
                pv_err |= True
            mode = os.stat(pv)[ST_MODE]
            if S_ISBLK(mode):
                continue
            elif S_ISREG(mode):
                cmd = [rcEnv.syspaths.losetup, '-j', pv]
                out, err, ret = justcall(cmd)
                if ret != 0 or not out.startswith('/dev/loop'):
                    self.r.log.error("pv %s is a regular file but not a loop"%pv)
                    pv_err |= True
                    continue
                self.pvs[i] = out.split(':')[0]
            else:
                self.r.log.error("pv %s is not a block device nor a loop file"%pv)
                pv_err |= True
        if pv_err:
            raise ex.excError

        for pv in self.pvs:
            if self.has_pv(pv):
                continue
            cmd = ['pvcreate', '-f', pv]
            ret, out, err = self.r.vcall(cmd)
            if ret != 0:
                raise ex.excError

        if len(self.pvs) == 0:
            raise ex.excError("no pvs specified")

        if self.r.has_it():
            self.r.log.info("vg %s already exists")
            return

        cmd = ['vgcreate', self.r.name] + self.pvs
        ret, out, err = self.r.vcall(cmd)
        if ret != 0:
            raise ex.excError

        self.r.can_rollback = True
        self.r.clear_cache("vg.lvs")
        self.r.clear_cache("vg.lvs.attr")
        self.r.clear_cache("vg.tags")
        self.r.clear_cache("vg.pvs")
        self.r.svc.node.unset_lazy("devtree")
0707010005c8f2000081b40000000000000000000000015ab21754000003a6000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/provDiskZpool.py   import provisioning
import rcExceptions as ex

class Prov(provisioning.Prov):
    def __init__(self, r):
        provisioning.Prov.__init__(self, r)

    def is_provisioned(self):
        return self.r.has_it()

    def unprovisioner(self):
        if not self.r.is_up():
            self.r.start()
        cmd = ["zpool", "destroy", "-f", self.r.name]
        self.r.vcall(cmd)
        self.r.svc.node.unset_lazy("devtree")

    def stop(self):
        # a pool must be imported for destroy
        pass

    def provisioner(self):
        try:
            self.name = self.r.name
            self.vdev = self.r.svc.conf_get(self.r.rid, "vdev")
        except Exception as e:
            raise ex.excError(str(e))

        cmd = ["zpool", "create", "-m", "legacy", self.name] + self.vdev
        ret, _, _ = self.r.vcall(cmd)
        if ret == 0:
            self.r.can_rollback = True
        self.r.svc.node.unset_lazy("devtree")

  0707010005c8f3000081b40000000000000000000000015ab2175400001546000000660000000000000000000000000000002500000000root/usr/share/opensvc/lib/provFs.py  import provisioning
from rcUtilities import justcall, which, protected_dir
from rcGlobalEnv import rcEnv
import os
import rcExceptions as ex
import shutil

class Prov(provisioning.Prov):
    # required from child classes:
    #   mkfs = ['mkfs.ext4', '-F']
    #   info = ['tune2fs', '-l']

    def __init__(self, r):
        provisioning.Prov.__init__(self, r)

    def check_fs(self):
        if not hasattr(self, "info"):
            return True
        cmd = self.info + [self.mkfs_dev]
        out, err, ret = justcall(cmd)
        if ret == 0:
            return True
        self.r.log.info("%s is not formatted"%self.mkfs_dev)
        return False

    def provision_dev(self):
        if rcEnv.sysname == 'Linux':
            p = __import__("provDiskLvLinux")
        elif rcEnv.sysname == 'HP-UX':
            p = __import__("provDiskLvHP-UX")
        else:
            return
        p.Prov(self.r).provisioner()

    def unprovision_dev(self):
        if rcEnv.sysname == 'Linux':
            p = __import__("provDiskLvLinux")
        else:
            return
        p.Prov(self.r).unprovisioner()

    def is_provisioned(self):
        if "bind" in self.r.mount_options:
            return True
        try:
            self.dev = self.r.conf_get("dev")
            self.mnt = self.r.conf_get("mnt")
        except ex.OptNotFound:
            return
        if self.dev is None:
            return
        if not os.path.exists(self.mnt):
            return False
        if self.r.fs_type in self.r.netfs:
            return True
        self.get_mkfs_dev()
        if not os.path.exists(self.dev) and not os.path.exists(self.mkfs_dev):
            return False
        return self.check_fs()

    def get_mkfs_dev(self):
        self.mkfs_dev = self.dev
        if rcEnv.sysname == 'HP-UX':
            l = self.dev.split('/')
            l[-1] = 'r'+l[-1]
            self.mkfs_dev = '/'.join(l)
            if not os.path.exists(self.mkfs_dev):
                raise ex.excError("%s raw device does not exists"%self.mkfs_dev)
        elif rcEnv.sysname == 'Darwin':
            if os.path.isfile(self.mkfs_dev):
                from rcLoopDarwin import file_to_loop
                devs = file_to_loop(self.mkfs_dev)
                if len(devs) == 1:
                    self.mkfs_dev = devs[0]
                else:
                    raise ex.excError("unable to find a device associated to %s" % self.mkfs_dev)
        elif rcEnv.sysname == 'Linux':
            if os.path.isfile(self.mkfs_dev):
                from rcLoopLinux import file_to_loop
                devs = file_to_loop(self.mkfs_dev)
                if len(devs) == 1:
                    self.mkfs_dev = devs[0]
                else:
                    raise ex.excError("unable to find a device associated to %s" % self.mkfs_dev)

    def provisioner_fs(self):
        if self.r.fs_type in self.r.netfs:
            return

        self.dev = self.r.conf_get("dev")
        self.mnt = self.r.conf_get("mnt")

        if self.dev is None:
            raise ex.excError("device not found. parent resource is down ?")
        if not os.path.exists(self.mnt):
            os.makedirs(self.mnt)
            self.r.log.info("%s mount point created"%self.mnt)

        if not os.path.exists(self.dev) and self.r.fs_type not in self.r.netfs:
            try:
                self.r.conf_get("vg", verbose=False)
                self.provision_dev()
            except ValueError:
                # keyword not supported (ex. bind mounts)
                pass
            except ex.OptNotFound:
                pass

        self.get_mkfs_dev()

        if not os.path.exists(self.mkfs_dev):
            raise ex.excError("abort fs provisioning: %s does not exist" % self.mkfs_dev)

        if self.check_fs():
            self.r.log.info("already provisioned")
            return

        if hasattr(self, "do_mkfs"):
            self.do_mkfs()
        elif hasattr(self, "mkfs"):
            try:
                opts = self.r.svc.conf_get(self.r.rid, "mkfs_opt")
            except:
                opts = []
            cmd = self.mkfs + opts + [self.mkfs_dev]
            (ret, out, err) = self.r.vcall(cmd)
            if ret != 0:
                self.r.log.error('Failed to format %s'%self.mkfs_dev)
                raise ex.excError
        else:
            raise ex.excError("no mkfs method implemented")

    def provisioner(self):
        self.r.unset_lazy("device")
        self.r.unset_lazy("label")
        if "bind" in self.r.mount_options:
            return
        self.provisioner_fs()

    def purge_mountpoint(self):
        if os.path.exists(self.r.mount_point) and not protected_dir(self.r.mount_point):
            self.r.log.info("rm -rf %s" % self.r.mount_point)
            try:
                shutil.rmtree(self.r.mount_point)
            except Exception as e:
                raise ex.excError(str(e))

    def unprovisioner_fs(self):
        pass

    def unprovisioner(self):
        if self.r.fs_type in self.r.netfs:
            return
        self.unprovisioner_fs()
        self.purge_mountpoint()
        if self.r.fs_type not in self.r.netfs:
            try:
                self.r.conf_get("vg", verbose=False)
                self.unprovision_dev()
            except ValueError:
                # keyword not supported (ex. bind mounts)
                return
            except ex.OptNotFound:
                pass


  0707010005c8f4000081b40000000000000000000000015ab2175400000ee3000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/provFsBtrfs.py import provFs
import tempfile
import os
import time
import rcExceptions as ex
from rcUtilities import which, justcall, lazy
from rcBtrfs import Btrfs

class Prov(provFs.Prov):
    info = ['btrfs', 'device', 'ready']

    @lazy
    def mkfs(self):
        return ['mkfs.btrfs', '-f', '-L', self.label]

    @lazy
    def raw_label(self):
        return '{svcname}.' + self.r.rid.replace("#", ".")

    @lazy
    def label(self):
        return self.r.svc.svcname + '.' + self.r.rid.replace("#", ".")

    def is_provisioned(self):
        ret = provFs.Prov.is_provisioned(self)
        if not ret:
            return ret
        if self.subvol is None:
            return ret
        mnt = tempfile.mkdtemp()
        self.mount(mnt)
        try:
            btrfs = Btrfs(path=mnt)
            return btrfs.has_subvol()
        finally:
            self.cleanup(mnt)

    def current_label(self, mnt):
        cmd = ["btrfs", "filesystem", "label", mnt]
        ret, out, err = self.r.call(cmd, errlog=False)
        if ret == 0 and len(out.strip()) > 0:
            return out.strip()

    @lazy
    def subvol(self):
        l = self.r.mount_options.split(",")
        for e in l:
            if not e.startswith("subvol="):
                continue
            subvol = e.replace("subvol=", "")
            return subvol

    def cleanup(self, mnt):
        cmd = ["umount", mnt]
        self.r.vcall(cmd)
        os.removedirs(mnt)

    def write_label(self, mnt):
        current_label = self.current_label(mnt)
        if current_label is not None:
            label = current_label
            raw_label = current_label.replace(self.r.svc.svcname, "{svcname}")
        else:
            label = self.label
            raw_label = self.raw_label
        self.r.svc.config.set(self.r.rid, "dev", "LABEL="+raw_label)
        self.r.svc.write_config()
        self.r.unset_lazy("device")
        self.wait_label(label)

    def wait_label(self, label):
        if which("findfs") is None:
            self.r.log.info("findfs program not found, wait arbitrary 20 seconds for label to be usable")
            time.sleep(20)
        cmd = ["findfs", "LABEL="+label]
        for i in range(20):
            out, err, ret = justcall(cmd)
            self.r.log.debug("%s\n%s\n%s" % (" ".join(cmd), out, err))
            if ret == 0:
                return
            self.r.log.info("label is not usable yet (%s)" % err.strip())
            time.sleep(2)
        raise ex.excError("timeout waiting for label to become usable")

    def mount(self, mnt):
        self.r.set_loopdevice()
        if self.r.loopdevice is None:
            device = self.r.device
        else:
            device = self.r.loopdevice
        cmd = ["mount", "-t", "btrfs", "-o", "subvolid=0", device, mnt]
        ret, out, err = self.r.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def create_subvol(self):
        if self.subvol is None:
            return
        mnt = tempfile.mkdtemp()
        self.mount(mnt)
        try:
            self.write_label(mnt)
            self._create_subvol(mnt)
            self.r.log.info("subvolume %s provisioned" % self.subvol)
            self.r.start()
        finally:
            self.cleanup(mnt)

    def _create_subvol(self, mnt):
        path = os.path.join(mnt, self.subvol)
        if os.path.exists(path):
            return
        cmd = ["btrfs", "subvol", "create", path]
        ret, out, err = self.r.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def provisioner(self):
        if self.r.device.startswith("LABEL=") or self.r.device.startswith("UUID="):
            self.r.log.info("skip formatting because dev is specified by LABEL or UUID")
        else:
            provFs.Prov.provisioner_fs(self)
        self.create_subvol()



 0707010005c8f5000081b40000000000000000000000015ab2175400000107000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/provFsDir.py   import provisioning

class Prov(provisioning.Prov):
    def __init__(self, r):
        provisioning.Prov.__init__(self, r)
    
    def is_provisioned(self):
        return True

    def provisioner(self):
        pass

    def unprovisioner(self):
        pass

 0707010005c8f6000081b40000000000000000000000015ab21754000004a2000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/provFsDocker.py    import sys
import os

import provisioning
from rcGlobalEnv import rcEnv
import rcExceptions as ex

if rcEnv.paths.pathcomposvc not in sys.path:
    sys.path.append(rcEnv.paths.pathcomposvc)

from file import CompFiles

class Prov(provisioning.Prov):
    def __init__(self, r):
        provisioning.Prov.__init__(self, r)

    def is_provisioned(self):
        return self.r.has_it()

    def provisioner(self):
        self.r.create_vol()
        self.populate()

    def populate(self):
        try:
            modulesets = self.r.conf_get("populate")
        except ex.OptNotFound as exc:
            return
        try:
            os.environ["OPENSVC_VOL_PATH"] = self.r.vol_path
            self.r.svc.compliance.options.moduleset = ",".join(modulesets)
            ret = self.r.svc.compliance.do_run("fix")
        finally:
            del os.environ["OPENSVC_VOL_PATH"]
        if ret:
            raise ex.excError

    def unprovisioner(self):
        if not self.r.has_it():
            return
        cmd = self.r.svc.dockerlib.docker_cmd + ["volume", "rm", "-f", self.r.volname]
        ret, out, err = self.r.vcall(cmd)
        if ret != 0:
            raise ex.excError

  0707010005c8f7000081b40000000000000000000000015ab217540000006a000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/provFsExt2.py  import provFs

class Prov(provFs.Prov):
    mkfs = ['mkfs.ext2', '-F', '-q']
    info = ['tune2fs', '-l']
  0707010005c8f8000081b40000000000000000000000015ab217540000006a000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/provFsExt3.py  import provFs

class Prov(provFs.Prov):
    mkfs = ['mkfs.ext3', '-F', '-q']
    info = ['tune2fs', '-l']
  0707010005c8f9000081b40000000000000000000000015ab217540000006a000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/provFsExt4.py  import provFs

class Prov(provFs.Prov):
    mkfs = ['mkfs.ext4', '-F', '-q']
    info = ['tune2fs', '-l']
  0707010005c8fa000081b40000000000000000000000015ab2175400000a15000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/provFsHfs.py   import provFs
import rcExceptions as ex
from rcUtilities import justcall

class Prov(provFs.Prov):
   mkfs = ['newfs_hfs']
   info = ['diskutil', 'info']

   def do_mkfs(self):
       try:
           opts = self.r.svc.conf_get(self.r.rid, "mkfs_opt").split()
       except:
           opts = []
       cmd = self.mkfs + opts + [self.mkfs_dev]
       (ret, out, err) = self.r.vcall(cmd)
       if ret != 0:
           self.r.log.error('Failed to format %s'%self.mkfs_dev)
           raise ex.excError
       self.r.start()
       cmd = ["diskutil", "enableOwnership", self.mkfs_dev]
       (ret, out, err) = self.r.vcall(cmd)
       if ret != 0:
           raise ex.excError

   def check_fs(self):
       """
          Device Identifier:        disk2
          Device Node:              /dev/disk2
          Whole:                    Yes
          Part of Whole:            disk2
          Device / Media Name:      Disk Image
       
          Volume Name:              Not applicable (no file system)
       
          Mounted:                  Not applicable (no file system)
       
          File System:              None
       
          Content (IOContent):      None
          OS Can Be Installed:      No
          Media Type:               Generic
          Protocol:                 Disk Image
          SMART Status:             Not Supported
       
          Total Size:               21.5 GB (21474836480 Bytes) (exactly 41943040 512-Byte-Units)
          Volume Free Space:        Not applicable (no file system)
          Device Block Size:        512 Bytes
       
          Read-Only Media:          No
          Read-Only Volume:         Not applicable (no file system)
       
          Device Location:          External
          Removable Media:          Yes
          Media Removal:            Software-Activated
       
          Virtual:                  Yes
          OS 9 Drivers:             No
          Low Level Format:         Not supported
       """
       cmd = self.info + [self.mkfs_dev]
       out, err, ret = justcall(cmd)
       if ret != 0:
           raise ex.excError(err)
       for line in out.splitlines():
           line = line.strip()
           if not line.startswith("File System:") and not line.startswith("File System Personality:"):
               continue
           if line.split()[-1] == "None":
               self.r.log.info("%s is not formatted", self.mkfs_dev)
               return False
           else:
               return True
       raise ex.excError("unable to determine if %s is formatted" % self.mkfs_dev)
   0707010005c8fb000081b40000000000000000000000015ab2175400000082000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/provFsVxfs.py  import provFs

class Prov(provFs.Prov):
    mkfs = ['newfs', '-F', 'vxfs', '-o', 'largefiles', '-b', '8192']
    info = ['fstyp']
  0707010005c8fc000081b40000000000000000000000015ab217540000006c000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/provFsXfs.py   import provFs

class Prov(provFs.Prov):
    info = ['xfs_admin', '-l']
    mkfs = ['mkfs.xfs', '-f', '-q']

0707010005c8fd000081b40000000000000000000000015ab2175400000812000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/provFsZfs.py   """
The zfs dataset provisioning driver.

Supports the provisioning keywords:

* size
* mkfs_opt
"""
import os
import provFs
from rcUtilities import which
from converters import convert_size
from rcGlobalEnv import rcEnv
from rcZfs import Dataset
import rcExceptions as ex

class Prov(provFs.Prov):
    """
    The zfs dataset provisioning class.
    """
    def unprovisioner(self):
        if not which(rcEnv.syspaths.zfs):
            self.r.log.error("zfs command not found")
            raise ex.excError
        dataset = Dataset(self.r.device, log=self.r.log)
        if dataset.exists():
            dataset.destroy(["-r"])
        if os.path.exists(self.r.mount_point) and os.path.isdir(self.r.mount_point):
            try:
                os.rmdir(self.r.mount_point)
                self.r.log.info("rmdir %s", self.r.mount_point)
            except OSError as exc:
                self.r.log.warning("failed to rmdir %s: %s", self.r.mount_point, exc)

    def provisioner(self):
        if not which(rcEnv.syspaths.zfs):
            self.r.log.error("zfs command not found")
            raise ex.excError
        dataset = Dataset(self.r.device, log=self.r.log)
        mkfs_opt = ["-p"]
        try:
            mkfs_opt += self.r.svc.conf_get(self.r.rid, "mkfs_opt")
        except ex.OptNotFound:
            pass

        if not any([True for e in mkfs_opt if e.startswith("mountpoint=")]):
            mkfs_opt += ['-o', 'mountpoint='+self.r.mount_point]
        if not any([True for e in mkfs_opt if e.startswith("canmount=")]):
            mkfs_opt += ['-o', 'canmount=noauto']

        if dataset.exists() is False:
            dataset.create(mkfs_opt)

        nv_list = dict()
        try:
            size = self.r.svc.conf_get(self.r.rid, "size")
        except ex.OptNotFound:
            return
        if size:
            nv_list['refquota'] = "%dM" % convert_size(size, _to="m")
        dataset.verify_prop(nv_list)

    def is_provisioned(self):
        dataset = Dataset(self.r.device, log=self.r.log)
        return dataset.exists()
  0707010005c8fe000081b40000000000000000000000015ab2175400000405000000660000000000000000000000000000002500000000root/usr/share/opensvc/lib/provIp.py  import provisioning
import rcExceptions as ex
from rcUtilities import lazy

class Prov(provisioning.Prov):
    def __init__(self, r):
        provisioning.Prov.__init__(self, r)
    
    def is_provisioned(self):
        try:
            self.r.conf_get("ipname")
            return True
        except ex.OptNotFound:
            return False

    @lazy
    def kw_provisioner(self):
        try:
            provisioner = self.r.conf_get("provisioner")
        except ex.OptNotFound as exc:
            provisioner = exc.default
        return provisioner

    def provisioner(self):
        """
        Provision the ip resource, allocate an ip collector's side, and
        start it.
        """
        if self.kw_provisioner != "collector":
            return
        self.r.allocate()

    def unprovisioner(self):
        """
        Unprovision the ip resource, meaning unplumb and release collector's
        side.
        """
        if self.kw_provisioner != "collector":
            return
        self.r.release()

   0707010005c8ff000081b40000000000000000000000015ab2175400000cda000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/provIpAmazon.py    import provisioning
import rcExceptions as ex

class Prov(provisioning.Prov):
    def __init__(self, r):
        provisioning.Prov.__init__(self, r)

    def provisioner(self):
        self.provisioner_private()
        self.provisioner_public()
        self.provisioner_docker_ip()
        self.cascade_allocation()
        self.r.log.info("provisioned")
        self.r.start()
        return True

    def cascade_allocation(self):
        if not self.r.svc.config.has_option(self.r.rid, "cascade_allocation"):
            return
        cascade = self.r.svc.config.get(self.r.rid, "cascade_allocation").split()
        need_write = False
        for e in cascade:
            try:
                rid, param = e.split(".")
            except:
                self.r.log.warning("misformatted cascade entry: %s (expected <rid>.<param>[@<scope>])" % e)
                continue
            if not self.r.svc.config.has_section(rid):
                self.r.log.warning("misformatted cascade entry: %s (rid does not exist)" % e)
                continue
            need_write = True
            self.r.log.info("cascade %s to %s" % (self.r.ipname, e))
            self.r.svc.config.set(rid, param, self.r.ipname)
            self.r.svc.resources_by_id[rid].ipname = self.r.svc.conf_get(rid, param)
            self.r.svc.resources_by_id[rid].addr = self.r.svc.resources_by_id[rid].ipname
        if need_write:
            self.r.svc.write_config()

    def provisioner_docker_ip(self):
        if not self.r.svc.config.has_option(self.r.rid, "docker_daemon_ip"):
            return
        if not self.r.svc.config.get(self.r.rid, "docker_daemon_ip"):
            return
        try:
            args = self.svc.conf_get('DEFAULT', 'docker_daemon_args')
        except ex.OptNotFound as exc:
            args = exc.default
        args += ["--ip", self.r.ipname]
        self.r.svc.config.set("DEFAULT", "docker_daemon_args", " ".join(args))
        self.r.svc.write_config()
        for r in self.r.svc.get_resources("container.docker"):
            # reload docker daemon args
            r.on_add()

    def provisioner_private(self):
        if self.r.ipname != "<allocate>":
            self.r.log.info("private ip already provisioned")
            return

        eni = self.r.get_network_interface()
        if eni is None:
            raise ex.excError("could not find ec2 network interface for %s" % self.ipdev)

        ips1 = set(self.r.get_instance_private_addresses())
        data = self.r.aws([
          "ec2", "assign-private-ip-addresses",
          "--network-interface-id", eni,
          "--secondary-private-ip-address-count", "1"
        ])
        ips2 = set(self.r.get_instance_private_addresses())
        new_ip = list(ips2 - ips1)[0]

        self.r.svc.config.set(self.r.rid, "ipname", new_ip)
        self.r.svc.write_config()
        self.r.ipname = new_ip

    def provisioner_public(self):
        if self.r.eip != "<allocate>":
            self.r.log.info("public ip already provisioned")
            return

        data = self.r.aws([
          "ec2", "allocate-address",
          "--domain", "vpc",
        ])

        self.r.svc.config.set(self.r.rid, "eip", data["PublicIp"])
        self.r.svc.write_config()
        self.r.eip = data["PublicIp"]

  0707010005c900000081b40000000000000000000000015ab21754000011a5000000660000000000000000000000000000002600000000root/usr/share/opensvc/lib/provKvm.py import provisioning
from rcGlobalEnv import rcEnv
from rcUtilities import which
import os
import rcExceptions as ex

class Prov(provisioning.Prov):
    def __init__(self, r):
        provisioning.Prov.__init__(self, r)

        self.section = r.svc.config.defaults()

        if 'snapof' in self.section:
            self.snapof = self.section['snapof']
        else:
            self.snapof = None

        if 'snap' in self.section:
            self.snap = self.section['snap']
        else:
            self.snap = None

        if 'virtinst' in self.section:
            self.virtinst = self.section['virtinst']
        else:
            self.virtinst = None

    def check_kvm(self):
        if os.path.exists(self.r.cf):
            return True
        return False

    def setup_kvm(self):
        if self.virtinst is None:
            self.r.log.error("the 'virtinst' parameter must be set")
            raise ex.excError
        ret, out, err = self.r.vcall(self.virtinst.split())
        if ret != 0:
            raise ex.excError

    def setup_ips(self):
        self.purge_known_hosts()
        for resource in self.r.svc.get_resources("ip"):
            self.purge_known_hosts(resource.addr)

    def purge_known_hosts(self, ip=None):
        if ip is None:
            cmd = ['ssh-keygen', '-R', self.r.svc.svcname]
        else:
            cmd = ['ssh-keygen', '-R', ip]
        ret, out, err = self.r.vcall(cmd, err_to_info=True)

    def setup_snap(self):
        if self.snap is None:
            self.r.log.error("the 'snap' parameter must be set")
            raise ex.excError
        if self.snapof is None:
            self.r.log.error("the 'snapof' parameter must be set")
            raise ex.excError
        if not which('btrfs'):
            self.r.log.error("'btrfs' command not found")
            raise ex.excError

        cmd = ['btrfs', 'subvolume', 'snapshot', self.snapof, self.snap]
        ret, out, err = self.r.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def get_pubkey(self):
        p = os.path.join(os.sep, 'root', '.ssh', 'id_dsa.pub')
        try:
            with open(p) as f:
                pub = f.read(8000)
        except:
            self.r.log.error('failed to read root public key')
            raise ex.excError
        return pub

    def get_gw(self):
        cmd = ['route', '-n']
        ret, out, err = self.r.call(cmd)
        if ret != 0:
            self.r.log.error('failed to read routing table')
            raise ex.excError
        for line in out.split('\n'):
            if line.startswith('0.0.0.0'):
                l = line.split()
                if len(l) > 1:
                    return l[1]
        self.r.log.error('failed to find default gateway')
        raise ex.excError

    def get_ns(self):
        p = os.path.join(os.sep, 'etc', 'resolv.conf')
        with open(p) as f:
            for line in f.readlines():
                if 'nameserver' in line:
                    l = line.split()
                    if len(l) > 1:
                        return l[1]
        self.r.log.error('failed to find a nameserver')
        raise ex.excError

    def get_config(self):
        cf = ['todo']
        s = ';'.join(('vm', self.r.name))
        cf.append(s)
        s = 'ns;192.168.122.1'
        cf.append(s)
        s = ';'.join(('gw', self.get_gw()))
        cf.append(s)
        try:
            s = ';'.join(('hv_root_pubkey', self.get_pubkey()))
            cf.append(s)
        except ex.excError:
            pass
        for resource in self.r.svc.get_resources("ip"):
            s = ';'.join((resource.rid, resource.ipdev, resource.addr, resource.mask))
            cf.append(s)
        cf.append('')
        return '\n'.join(cf)

    def setup_cfdisk(self):
        config = self.get_config()
        block = len(config)//512 + 1
        cfdisk = os.path.join(rcEnv.paths.pathtmp, self.r.svc.svcname+'.cfdisk')
        try:
            with open(cfdisk, 'w') as f:
                f.write(config)
                f.seek(block*512)
                f.write('\0')
        except:
            self.r.log.error("failed to create config disk")
            raise ex.excError
        self.virtinst += " --disk path=%s,device=floppy"%cfdisk
        self.r.log.info("created config disk with content;\n%s", config)

    def provisioner(self):
        self.setup_snap()
        self.setup_cfdisk()
        self.setup_kvm()
        self.setup_ips()
        self.r.log.info("provisioned")
        return True
   0707010005c901000081b40000000000000000000000015ab217540000219b000000660000000000000000000000000000002600000000root/usr/share/opensvc/lib/provLxc.py import provisioning
from rcGlobalEnv import rcEnv
import os
import rcExceptions as ex

class Prov(provisioning.Prov):
    config_template = """\
lxc.utsname = %(vm_name)s
lxc.tty = 4
lxc.pts = 1024
lxc.console = /var/log/opensvc/%(vm_name)s.console.log

lxc.rootfs = %(rootfs)s
lxc.cgroup.devices.deny = a
# /dev/null and zero
lxc.cgroup.devices.allow = c 1:3 rwm
lxc.cgroup.devices.allow = c 1:5 rwm
# consoles
lxc.cgroup.devices.allow = c 5:1 rwm
lxc.cgroup.devices.allow = c 5:0 rwm
lxc.cgroup.devices.allow = c 4:0 rwm
lxc.cgroup.devices.allow = c 4:1 rwm
# /dev/{,u}random
lxc.cgroup.devices.allow = c 1:9 rwm
lxc.cgroup.devices.allow = c 1:8 rwm
lxc.cgroup.devices.allow = c 136:* rwm
lxc.cgroup.devices.allow = c 5:2 rwm
# rtc
lxc.cgroup.devices.allow = c 254:0 rwm

lxc.network.type = veth
lxc.network.flags = up
lxc.network.link = br0
lxc.network.name = eth0
lxc.network.mtu = 1500

# mounts point
lxc.mount.entry=proc %(rootfs)s/proc proc nodev,noexec,nosuid 0 0
lxc.mount.entry=devpts %(rootfs)s/dev/pts devpts defaults 0 0
lxc.mount.entry=sysfs %(rootfs)s/sys sysfs defaults 0 0
"""
    def __init__(self, r):
        provisioning.Prov.__init__(self, r)

        self.section = r.svc.config.defaults()
        try:
            self.rootfs = self.r.svc.conf_get(self.r.rid, 'rootfs')
        except ex.OptNotFound:
            self.rootfs = None
            return

        try:
            self.template = self.r.svc.conf_get(self.r.rid, 'template')
        except ex.OptNotFound:
            self.template =None
            return

        # hostname file in the container rootfs
        self.p_hostname = os.path.join(self.rootfs, 'etc', 'hostname')

        self.vm_name = r.name

        # lxc root conf dir
        self.d_lxc = os.path.join(os.sep, 'var', 'lib', 'lxc')
        if not os.path.exists(self.d_lxc):
            self.d_lxc = os.path.join(os.sep, 'usr', 'local', 'var', 'lib', 'lxc')
        if not os.path.exists(self.d_lxc):
            self.d_lxc = None

        # container config file
        if self.d_lxc is not None:
            self.config = os.path.join(self.d_lxc, self.vm_name, 'config')

        # network config candidates
        self.interfaces = os.path.join(self.rootfs, 'etc', 'network', 'interfaces')
        self.network = os.path.join(self.rootfs, 'etc', 'sysconfig', 'network-scripts')

    def validate(self):
        if self.d_lxc is None:
            self.r.log.error("this node is not lxc capable")
            return True

        if not self.check_vm_name():
            return False

        if not self.check_lxc():
            self.r.log.error("container is not created")
            return False

        return True

    def check_lxc(self):
        if os.path.exists(self.r.cf):
            return True
        return False

    def setup_lxc_config(self):
        import tempfile
        f = tempfile.NamedTemporaryFile(delete=False)
        f.write(self.config_template%dict(vm_name=self.vm_name, rootfs=self.rootfs))
        self.config = f.name
        f.close()

    def setup_lxc(self):
        if self.check_lxc():
            self.r.log.info("container is already created")
            return
        name = self.setup_lxc_config()
        with open(os.path.join(rcEnv.paths.pathlog, "%s.console.log"%self.vm_name), "a+") as f:
            f.write("")
        cmd = ['lxc-create', '-n', self.vm_name, '-f', self.config]
        (ret, out, err) = self.r.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def check_vm_name(self):
        if not os.path.exists(self.p_hostname):
            return False

        try:
            with open(self.p_hostname) as f:
                h = f.read().strip()
        except:
            self.r.log.error("can not get container hostname")
            raise ex.excError

        if h != self.vm_name:
            self.r.log.info("container hostname is not %s"%self.vm_name)
            return False

        return True

    def set_vm_name(self):
        if self.check_vm_name():
            self.r.log.info("container hostname already set")
            return
        with open(self.p_hostname, 'w') as f:
            f.write(self.vm_name+'\n')
        self.r.log.info("container hostname set to %s"%self.vm_name)

    def get_template(self):
        self.template_fname = os.path.basename(self.template)
        self.template_local = os.path.join(rcEnv.paths.pathtmp, self.template_fname)
        if os.path.exists(self.template_local):
            self.r.log.info("template %s already downloaded"%self.template_fname)
            return
        import sys
        try:
            self.r.svc.node.urlretrieve(template, self.template_local)
        except IOError as e:
            self.r.log.error("download failed", ":", e)
            try:
                os.unlink(self.template_local)
            except:
                pass
            raise ex.excError

    def unpack_template(self):
        import tarfile
        os.chdir(self.rootfs)
        tar = tarfile.open(name=self.template_local, errorlevel=0)
        if os.path.exists(os.path.join(self.rootfs,'etc')):
            self.r.log.info("template already unpacked")
            return
        tar.extractall()
        tar.close()

    def setup_template(self):
        self.set_vm_name()

    def disable_udev(self):
        updaterc = os.path.join(self.rootfs, 'usr', 'sbin', 'update-rc.d')
        chkconfig = os.path.join(self.rootfs, 'sbin', 'chkconfig')
        if os.path.exists(updaterc):
            self.r.vcall(['chroot', self.rootfs, 'update-rc.d', '-f', 'udev', 'remove'])
        elif os.path.exists(chkconfig):
            self.r.vcall(['chroot', self.rootfs, 'chkconfig', 'udev', 'off'])

    def setup_getty(self):
         getty = os.path.join(self.rootfs, 'sbin', 'getty')
         if os.path.exists(getty):
             self.r.log.info("setup getty")
             inittab = os.path.join(self.rootfs, 'etc', 'inittab')
             with open(inittab, 'a') as f:
                 f.write("""
1:2345:respawn:/sbin/getty 38400 console
c1:12345:respawn:/sbin/getty 38400 tty1 linux
""")

    def setup_authkeys(self):
        pub = os.path.join(os.sep, 'root', '.ssh', 'id_dsa.pub')
        authkeys = os.path.join(self.rootfs, 'root', '.ssh', 'authorized_keys')
        if not os.path.exists(pub):
            self.r.log.error("no dsa found on node for root")
            return
        import shutil
        if not os.path.exists(os.path.dirname(authkeys)):
            os.makedirs(os.path.dirname(authkeys))
        shutil.copyfile(pub, authkeys)
        os.chmod(authkeys, int('600', 8))
        self.r.log.info("setup hypervisor root trust")

    def setup_ip(self, r):
        self.purge_known_hosts(r.addr)
        if os.path.exists(self.interfaces):
            return self.setup_ip_debian(r)
        elif os.path.exists(self.network):
            return self.setup_ip_rh(r)

    def setup_ip_rh(self, r):
        r.getaddr()
        buff = """
DEVICE=%(ipdev)s
IPADDR=%(ipaddr)s
NETMASK=%(netmask)s
ONBOOT=yes
"""%dict(ipdev=r.ipdev, netmask=r.mask, ipaddr=r.addr)
        intf = os.path.join(self.network, 'ifcfg-'+r.ipdev)
        with open(intf, 'w') as f:
            f.write(buff)

    def setup_ip_debian(self, r):
        r.getaddr()
        buff = """
auto lo
iface lo inet loopback

auto %(ipdev)s
iface %(ipdev)s inet static
    address %(ipaddr)s
    netmask %(netmask)s

"""%dict(ipdev=r.ipdev, netmask=r.mask, ipaddr=r.addr)
        with open(self.interfaces, 'w') as f:
            f.write(buff)

    def setup_ips(self):
        self.purge_known_hosts()
        for resource in self.r.svc.get_resources("ip"):
            self.setup_ip(resource)

    def purge_known_hosts(self, ip=None):
        if ip is None:
            cmd = ['ssh-keygen', '-R', self.r.svc.svcname]
        else:
            cmd = ['ssh-keygen', '-R', ip]
        ret, out, err = self.r.vcall(cmd, err_to_info=True)

    def provisioner(self):
        if self.rootfs is None:
            self.r.log.info("missing provisioning keyword: rootfs. skip")
            return
        if self.template is None:
            self.r.log.info("missing provisioning keyword: template. skip")
            return
        path = self.rootfs

        if not os.path.exists(path):
            os.makedirs(path)

        self.get_template()
        self.unpack_template()
        self.setup_template()
        self.setup_lxc()
        self.disable_udev()
        self.setup_getty()
        self.setup_authkeys()
        self.setup_ips()

        self.r.start()
        self.r.log.info("provisioned")
        return True
 0707010005c902000081b40000000000000000000000015ab21754000000bd000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/provShare.py   import provisioning
import rcExceptions as ex

class Prov(provisioning.Prov):
    def __init__(self, r):
        provisioning.Prov.__init__(self, r)
    
    def start(self):
        pass

   0707010005c903000081b40000000000000000000000015ab2175400000b7c000000660000000000000000000000000000002600000000root/usr/share/opensvc/lib/provSrp.py import provisioning
from rcGlobalEnv import rcEnv
import os
import socket
import rcExceptions as ex

class Prov(provisioning.Prov):
    def __init__(self, r):
        provisioning.Prov.__init__(self, r)

        self.name = r.name
        self.rootpath = os.path.join(os.sep, 'var', 'hpsrp', self.name)
        try:
            self.prm_cores = r.svc.config.get(r.rid, 'prm_cores')
        except:
            self.prm_cores = "1"
        self.ip = r.svc.config.get(r.rid, 'ip')
        self.ip = self.lookup(self.ip)
        self.need_start = []

    def lookup(self, ip):
        try:
            int(ip[0])
            # already in cidr form
            return
        except:
            pass

        try:
            a = socket.getaddrinfo(ip, None)
            if len(a) == 0:
                raise Exception
            ip = a[0][-1][0]
            return ip
        except:
            raise ex.excError("could not resolve %s to an ip address"%self.ip)

    def validate(self):
        # False triggers provisioner, True skip provisioner
        if not which('srp'):
            self.r.log.error("this node is not srp capable")
            return True

        if self.check_srp():
            self.r.log.error("container is already created")
            return True

        return False

    def check_srp(self):
        try:
            self.r.get_status()
        except:
            return False
        return True

    def cleanup(self):
        rs = self.r.svc.get_resources('fs')
        rs.sort(key=lambda x: x.mount_point, reverse=True)
        for r in rs:
            if r.mount_point == self.rootpath:
                continue
            if not r.mount_point.startswith(self.rootpath):
                continue
            r.stop()
            self.need_start.append(r)
            os.unlink(r.mount_point)
            p = r.mount_point
            while True:
                p = os.path.realpath(os.path.join(p, '..'))
                if p == self.rootpath:
                    break
                try:
                    self.r.log.info("unlink %s"%p)
                    os.unlink(p)
                except:
                    break

    def restart_fs(self):
        for r in self.need_start:
            r.start()

    def add_srp(self):
        self.cleanup()
        cmd = ['srp', '-batch',
               '-a', self.name,
               '-t', 'system',
               '-s', 'admin,cmpt,init,prm,network',
               'ip_address='+self.ip, 'assign_ip=no',
               'autostart=no',
               'delete_files_ok=no',
               'root_password=""',
               'prm_group_type=PSET',
               'prm_cores='+str(self.prm_cores)]
        ret, out, err = self.r.vcall(cmd)
        if ret != 0:
            raise ex.excError()
        self.restart_fs()

    def provisioner(self):
        self.add_srp()
        self.r.start()
        self.r.log.info("provisioned")
        return True
0707010005c904000081b40000000000000000000000015ab21754000003a9000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/provisioning.py    class Prov(object):
    def __init__(self, r):
        self.r = r

    def start(self):
        self.r.start()

    def stop(self):
        self.r.stop()

    def is_provisioned(self):
        return

    def unprovisioner(self):
        pass

    def provisioner(self):
        pass

    def remove_keywords(self, keywords=[]):
        for kw in keywords:
            self.remove_keyword(kw, write=False)
        self.r.svc.write_config()

    def remove_keyword(self, keyword, write=True):
        for o in self.r.svc.config.options(self.r.rid):
            if o != keyword and not o.startswith(keyword+"@"):
                continue
            self.r.log.info("comment out provisioning keyword: %s" % o)
            val = self.r.svc.config.get(self.r.rid, o)
            self.r.svc.config.remove_option(self.r.rid, o)
            self.r.svc.config.set(self.r.rid, "#"+o, val)

        if write:
            self.r.svc.write_config()
   0707010005c905000041ed0000000000000000000000025ab2183a00000000000000660000000000000000000000000000002100000000root/usr/share/opensvc/lib/pyaes  0707010005c906000081b40000000000000000000000015ab2175400000827000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/pyaes/__init__.py  # The MIT License (MIT)
#
# Copyright (c) 2014 Richard Moore
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

# This is a pure-Python implementation of the AES algorithm and AES common
# modes of operation.

# See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
# See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation


# Supported key sizes:
#   128-bit
#   192-bit
#   256-bit


# Supported modes of operation:
#   ECB - Electronic Codebook
#   CBC - Cipher-Block Chaining
#   CFB - Cipher Feedback
#   OFB - Output Feedback
#   CTR - Counter

# See the README.md for API details and general information.

# Also useful, PyCrypto, a crypto library implemented in C with Python bindings:
# https://www.dlitz.net/software/pycrypto/


VERSION = [1, 3, 0]

from .aes import AES, AESModeOfOperationCTR, AESModeOfOperationCBC, AESModeOfOperationCFB, AESModeOfOperationECB, AESModeOfOperationOFB, AESModesOfOperation, Counter
from .blockfeeder import decrypt_stream, Decrypter, encrypt_stream, Encrypter
from .blockfeeder import PADDING_NONE, PADDING_DEFAULT
 0707010005c907000081b40000000000000000000000015ab217540000eb96000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/pyaes/aes.py   # The MIT License (MIT)
#
# Copyright (c) 2014 Richard Moore
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

# This is a pure-Python implementation of the AES algorithm and AES common
# modes of operation.

# See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard

# Honestly, the best description of the modes of operations are the wonderful
# diagrams on Wikipedia. They explain in moments what my words could never
# achieve. Hence the inline documentation here is sparer than I'd prefer.
# See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation

# Also useful, PyCrypto, a crypto library implemented in C with Python bindings:
# https://www.dlitz.net/software/pycrypto/


# Supported key sizes:
#   128-bit
#   192-bit
#   256-bit


# Supported modes of operation:
#   ECB - Electronic Codebook
#   CBC - Cipher-Block Chaining
#   CFB - Cipher Feedback
#   OFB - Output Feedback
#   CTR - Counter


# See the README.md for API details and general information.


import copy
import struct

__all__ = ["AES", "AESModeOfOperationCTR", "AESModeOfOperationCBC", "AESModeOfOperationCFB",
           "AESModeOfOperationECB", "AESModeOfOperationOFB", "AESModesOfOperation", "Counter"]


def _compact_word(word):
    return (word[0] << 24) | (word[1] << 16) | (word[2] << 8) | word[3]

def _string_to_bytes(text):
    return list(ord(c) for c in text)

def _bytes_to_string(binary):
    return "".join(chr(b) for b in binary)

def _concat_list(a, b):
    return a + b


# Python 3 compatibility
try:
    xrange
except Exception:
    xrange = range

    # Python 3 supports bytes, which is already an array of integers
    def _string_to_bytes(text):
        if isinstance(text, bytes):
            return text
        return [ord(c) for c in text]

    # In Python 3, we return bytes
    def _bytes_to_string(binary):
        return bytes(binary)

    # Python 3 cannot concatenate a list onto a bytes, so we bytes-ify it first
    def _concat_list(a, b):
        return a + bytes(b)


# Based *largely* on the Rijndael implementation
# See: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
class AES(object):
    '''Encapsulates the AES block cipher.

    You generally should not need this. Use the AESModeOfOperation classes
    below instead.'''

    # Number of rounds by keysize
    number_of_rounds = {16: 10, 24: 12, 32: 14}

    # Round constant words
    rcon = [ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 ]

    # S-box and Inverse S-box (S is for Substitution)
    S = [ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ]
    Si =[ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d ] 

    # Transformations for encryption
    T1 = [ 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a ]
    T2 = [ 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616 ]
    T3 = [ 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16 ]
    T4 = [ 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c ]

    # Transformations for decryption
    T5 = [ 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742 ]
    T6 = [ 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857 ]
    T7 = [ 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8 ]
    T8 = [ 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0 ]

    # Transformations for decryption key expansion
    U1 = [ 0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3 ]
    U2 = [ 0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697 ]
    U3 = [ 0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46 ]
    U4 = [ 0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d ]

    def __init__(self, key):

        if len(key) not in (16, 24, 32):
            raise ValueError('Invalid key size')

        rounds = self.number_of_rounds[len(key)]

        # Encryption round keys
        self._Ke = [[0] * 4 for i in xrange(rounds + 1)]

        # Decryption round keys
        self._Kd = [[0] * 4 for i in xrange(rounds + 1)]

        round_key_count = (rounds + 1) * 4
        KC = len(key) // 4

        # Convert the key into ints
        tk = [ struct.unpack('>i', key[i:i + 4])[0] for i in xrange(0, len(key), 4) ]

        # Copy values into round key arrays
        for i in xrange(0, KC):
            self._Ke[i // 4][i % 4] = tk[i]
            self._Kd[rounds - (i // 4)][i % 4] = tk[i]

        # Key expansion (fips-197 section 5.2)
        rconpointer = 0
        t = KC
        while t < round_key_count:

            tt = tk[KC - 1]
            tk[0] ^= ((self.S[(tt >> 16) & 0xFF] << 24) ^
                      (self.S[(tt >>  8) & 0xFF] << 16) ^
                      (self.S[ tt        & 0xFF] <<  8) ^
                       self.S[(tt >> 24) & 0xFF]        ^
                      (self.rcon[rconpointer] << 24))
            rconpointer += 1

            if KC != 8:
                for i in xrange(1, KC):
                    tk[i] ^= tk[i - 1]

            # Key expansion for 256-bit keys is "slightly different" (fips-197)
            else:
                for i in xrange(1, KC // 2):
                    tk[i] ^= tk[i - 1]
                tt = tk[KC // 2 - 1]

                tk[KC // 2] ^= (self.S[ tt        & 0xFF]        ^
                               (self.S[(tt >>  8) & 0xFF] <<  8) ^
                               (self.S[(tt >> 16) & 0xFF] << 16) ^
                               (self.S[(tt >> 24) & 0xFF] << 24))

                for i in xrange(KC // 2 + 1, KC):
                    tk[i] ^= tk[i - 1]

            # Copy values into round key arrays
            j = 0
            while j < KC and t < round_key_count:
                self._Ke[t // 4][t % 4] = tk[j]
                self._Kd[rounds - (t // 4)][t % 4] = tk[j]
                j += 1
                t += 1

        # Inverse-Cipher-ify the decryption round key (fips-197 section 5.3)
        for r in xrange(1, rounds):
            for j in xrange(0, 4):
                tt = self._Kd[r][j]
                self._Kd[r][j] = (self.U1[(tt >> 24) & 0xFF] ^
                                  self.U2[(tt >> 16) & 0xFF] ^
                                  self.U3[(tt >>  8) & 0xFF] ^
                                  self.U4[ tt        & 0xFF])

    def encrypt(self, plaintext):
        'Encrypt a block of plain text using the AES block cipher.'

        if len(plaintext) != 16:
            raise ValueError('wrong block length')

        rounds = len(self._Ke) - 1
        (s1, s2, s3) = [1, 2, 3]
        a = [0, 0, 0, 0]

        # Convert plaintext to (ints ^ key)
        t = [(_compact_word(plaintext[4 * i:4 * i + 4]) ^ self._Ke[0][i]) for i in xrange(0, 4)]

        # Apply round transforms
        for r in xrange(1, rounds):
            for i in xrange(0, 4):
                a[i] = (self.T1[(t[ i          ] >> 24) & 0xFF] ^
                        self.T2[(t[(i + s1) % 4] >> 16) & 0xFF] ^
                        self.T3[(t[(i + s2) % 4] >>  8) & 0xFF] ^
                        self.T4[ t[(i + s3) % 4]        & 0xFF] ^
                        self._Ke[r][i])
            t = copy.copy(a)

        # The last round is special
        result = [ ]
        for i in xrange(0, 4):
            tt = self._Ke[rounds][i]
            result.append((self.S[(t[ i           ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF)
            result.append((self.S[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF)
            result.append((self.S[(t[(i + s2) % 4] >>  8) & 0xFF] ^ (tt >>  8)) & 0xFF)
            result.append((self.S[ t[(i + s3) % 4]        & 0xFF] ^  tt       ) & 0xFF)

        return result

    def decrypt(self, ciphertext):
        'Decrypt a block of cipher text using the AES block cipher.'

        if len(ciphertext) != 16:
            raise ValueError('wrong block length')

        rounds = len(self._Kd) - 1
        (s1, s2, s3) = [3, 2, 1]
        a = [0, 0, 0, 0]

        # Convert ciphertext to (ints ^ key)
        t = [(_compact_word(ciphertext[4 * i:4 * i + 4]) ^ self._Kd[0][i]) for i in xrange(0, 4)]

        # Apply round transforms
        for r in xrange(1, rounds):
            for i in xrange(0, 4):
                a[i] = (self.T5[(t[ i          ] >> 24) & 0xFF] ^
                        self.T6[(t[(i + s1) % 4] >> 16) & 0xFF] ^
                        self.T7[(t[(i + s2) % 4] >>  8) & 0xFF] ^
                        self.T8[ t[(i + s3) % 4]        & 0xFF] ^
                        self._Kd[r][i])
            t = copy.copy(a)

        # The last round is special
        result = [ ]
        for i in xrange(0, 4):
            tt = self._Kd[rounds][i]
            result.append((self.Si[(t[ i           ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF)
            result.append((self.Si[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF)
            result.append((self.Si[(t[(i + s2) % 4] >>  8) & 0xFF] ^ (tt >>  8)) & 0xFF)
            result.append((self.Si[ t[(i + s3) % 4]        & 0xFF] ^  tt       ) & 0xFF)

        return result


class Counter(object):
    '''A counter object for the Counter (CTR) mode of operation.

       To create a custom counter, you can usually just override the
       increment method.'''

    def __init__(self, initial_value = 1):

        # Convert the value into an array of bytes long
        self._counter = [ ((initial_value >> i) % 256) for i in xrange(128 - 8, -1, -8) ]

    value = property(lambda s: s._counter)

    def increment(self):
        '''Increment the counter (overflow rolls back to 0).'''

        for i in xrange(len(self._counter) - 1, -1, -1):
            self._counter[i] += 1

            if self._counter[i] < 256: break

            # Carry the one
            self._counter[i] = 0

        # Overflow
        else:
            self._counter = [ 0 ] * len(self._counter)


class AESBlockModeOfOperation(object):
    '''Super-class for AES modes of operation that require blocks.'''
    def __init__(self, key):
        self._aes = AES(key)

    def decrypt(self, ciphertext):
        raise Exception('not implemented')

    def encrypt(self, plaintext):
        raise Exception('not implemented')


class AESStreamModeOfOperation(AESBlockModeOfOperation):
    '''Super-class for AES modes of operation that are stream-ciphers.'''

class AESSegmentModeOfOperation(AESStreamModeOfOperation):
    '''Super-class for AES modes of operation that segment data.'''

    segment_bytes = 16



class AESModeOfOperationECB(AESBlockModeOfOperation):
    '''AES Electronic Codebook Mode of Operation.

       o Block-cipher, so data must be padded to 16 byte boundaries

   Security Notes:
       o This mode is not recommended
       o Any two identical blocks produce identical encrypted values,
         exposing data patterns. (See the image of Tux on wikipedia)

   Also see:
       o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_.28ECB.29
       o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.1'''


    name = "Electronic Codebook (ECB)"

    def encrypt(self, plaintext):
        if len(plaintext) != 16:
            raise ValueError('plaintext block must be 16 bytes')

        plaintext = _string_to_bytes(plaintext)
        return _bytes_to_string(self._aes.encrypt(plaintext))

    def decrypt(self, ciphertext):
        if len(ciphertext) != 16:
            raise ValueError('ciphertext block must be 16 bytes')

        ciphertext = _string_to_bytes(ciphertext)
        return _bytes_to_string(self._aes.decrypt(ciphertext))



class AESModeOfOperationCBC(AESBlockModeOfOperation):
    '''AES Cipher-Block Chaining Mode of Operation.

       o The Initialization Vector (IV)
       o Block-cipher, so data must be padded to 16 byte boundaries
       o An incorrect initialization vector will only cause the first
         block to be corrupt; all other blocks will be intact
       o A corrupt bit in the cipher text will cause a block to be
         corrupted, and the next block to be inverted, but all other
         blocks will be intact.

   Security Notes:
       o This method (and CTR) ARE recommended.

   Also see:
       o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29
       o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.2'''


    name = "Cipher-Block Chaining (CBC)"

    def __init__(self, key, iv = None):
        if iv is None:
            self._last_cipherblock = [ 0 ] * 16
        elif len(iv) != 16:
            raise ValueError('initialization vector must be 16 bytes')
        else:
            self._last_cipherblock = _string_to_bytes(iv)

        AESBlockModeOfOperation.__init__(self, key)

    def encrypt(self, plaintext):
        if len(plaintext) != 16:
            raise ValueError('plaintext block must be 16 bytes')

        plaintext = _string_to_bytes(plaintext)
        precipherblock = [ (p ^ l) for (p, l) in zip(plaintext, self._last_cipherblock) ]
        self._last_cipherblock = self._aes.encrypt(precipherblock)

        return _bytes_to_string(self._last_cipherblock)

    def decrypt(self, ciphertext):
        if len(ciphertext) != 16:
            raise ValueError('ciphertext block must be 16 bytes')

        cipherblock = _string_to_bytes(ciphertext)
        plaintext = [ (p ^ l) for (p, l) in zip(self._aes.decrypt(cipherblock), self._last_cipherblock) ]
        self._last_cipherblock = cipherblock

        return _bytes_to_string(plaintext)



class AESModeOfOperationCFB(AESSegmentModeOfOperation):
    '''AES Cipher Feedback Mode of Operation.

       o A stream-cipher, so input does not need to be padded to blocks,
         but does need to be padded to segment_size

    Also see:
       o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_.28CFB.29
       o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.3'''


    name = "Cipher Feedback (CFB)"

    def __init__(self, key, iv, segment_size = 1):
        if segment_size == 0: segment_size = 1

        if iv is None:
            self._shift_register = [ 0 ] * 16
        elif len(iv) != 16:
            raise ValueError('initialization vector must be 16 bytes')
        else:
          self._shift_register = _string_to_bytes(iv)

        self._segment_bytes = segment_size

        AESBlockModeOfOperation.__init__(self, key)

    segment_bytes = property(lambda s: s._segment_bytes)

    def encrypt(self, plaintext):
        if len(plaintext) % self._segment_bytes != 0:
            raise ValueError('plaintext block must be a multiple of segment_size')

        plaintext = _string_to_bytes(plaintext)

        # Break block into segments
        encrypted = [ ]
        for i in xrange(0, len(plaintext), self._segment_bytes):
            plaintext_segment = plaintext[i: i + self._segment_bytes]
            xor_segment = self._aes.encrypt(self._shift_register)[:len(plaintext_segment)]
            cipher_segment = [ (p ^ x) for (p, x) in zip(plaintext_segment, xor_segment) ]

            # Shift the top bits out and the ciphertext in
            self._shift_register = _concat_list(self._shift_register[len(cipher_segment):], cipher_segment)

            encrypted.extend(cipher_segment)

        return _bytes_to_string(encrypted)

    def decrypt(self, ciphertext):
        if len(ciphertext) % self._segment_bytes != 0:
            raise ValueError('ciphertext block must be a multiple of segment_size')

        ciphertext = _string_to_bytes(ciphertext)

        # Break block into segments
        decrypted = [ ]
        for i in xrange(0, len(ciphertext), self._segment_bytes):
            cipher_segment = ciphertext[i: i + self._segment_bytes]
            xor_segment = self._aes.encrypt(self._shift_register)[:len(cipher_segment)]
            plaintext_segment = [ (p ^ x) for (p, x) in zip(cipher_segment, xor_segment) ]

            # Shift the top bits out and the ciphertext in
            self._shift_register = _concat_list(self._shift_register[len(cipher_segment):], cipher_segment)

            decrypted.extend(plaintext_segment)

        return _bytes_to_string(decrypted)



class AESModeOfOperationOFB(AESStreamModeOfOperation):
    '''AES Output Feedback Mode of Operation.

       o A stream-cipher, so input does not need to be padded to blocks,
         allowing arbitrary length data.
       o A bit twiddled in the cipher text, twiddles the same bit in the
         same bit in the plain text, which can be useful for error
         correction techniques.

    Also see:
       o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_.28OFB.29
       o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.4'''


    name = "Output Feedback (OFB)"

    def __init__(self, key, iv = None):
        if iv is None:
            self._last_precipherblock = [ 0 ] * 16
        elif len(iv) != 16:
            raise ValueError('initialization vector must be 16 bytes')
        else:
          self._last_precipherblock = _string_to_bytes(iv)

        self._remaining_block = [ ]

        AESBlockModeOfOperation.__init__(self, key)

    def encrypt(self, plaintext):
        encrypted = [ ]
        for p in _string_to_bytes(plaintext):
            if len(self._remaining_block) == 0:
                self._remaining_block = self._aes.encrypt(self._last_precipherblock)
                self._last_precipherblock = [ ]
            precipherbyte = self._remaining_block.pop(0)
            self._last_precipherblock.append(precipherbyte)
            cipherbyte = p ^ precipherbyte
            encrypted.append(cipherbyte)

        return _bytes_to_string(encrypted)

    def decrypt(self, ciphertext):
        # AES-OFB is symetric
        return self.encrypt(ciphertext)



class AESModeOfOperationCTR(AESStreamModeOfOperation):
    '''AES Counter Mode of Operation.

       o A stream-cipher, so input does not need to be padded to blocks,
         allowing arbitrary length data.
       o The counter must be the same size as the key size (ie. len(key))
       o Each block independant of the other, so a corrupt byte will not
         damage future blocks.
       o Each block has a uniue counter value associated with it, which
         contributes to the encrypted value, so no data patterns are
         leaked.
       o Also known as: Counter Mode (CM), Integer Counter Mode (ICM) and
         Segmented Integer Counter (SIC

   Security Notes:
       o This method (and CBC) ARE recommended.
       o Each message block is associated with a counter value which must be
         unique for ALL messages with the same key. Otherwise security may be
         compromised.

    Also see:

       o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29
       o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.5
         and Appendix B for managing the initial counter'''


    name = "Counter (CTR)"

    def __init__(self, key, counter = None):
        AESBlockModeOfOperation.__init__(self, key)

        if counter is None:
            counter = Counter()

        self._counter = counter
        self._remaining_counter = [ ]

    def encrypt(self, plaintext):
        while len(self._remaining_counter) < len(plaintext):
            self._remaining_counter += self._aes.encrypt(self._counter.value)
            self._counter.increment()

        plaintext = _string_to_bytes(plaintext)

        encrypted = [ (p ^ c) for (p, c) in zip(plaintext, self._remaining_counter) ]
        self._remaining_counter = self._remaining_counter[len(encrypted):]

        return _bytes_to_string(encrypted)

    def decrypt(self, crypttext):
        # AES-CTR is symetric
        return self.encrypt(crypttext)


# Simple lookup table for each mode
AESModesOfOperation = dict(
    ctr = AESModeOfOperationCTR,
    cbc = AESModeOfOperationCBC,
    cfb = AESModeOfOperationCFB,
    ecb = AESModeOfOperationECB,
    ofb = AESModeOfOperationOFB,
)
  0707010005c908000081b40000000000000000000000015ab2175400001fb7000000660000000000000000000000000000003000000000root/usr/share/opensvc/lib/pyaes/blockfeeder.py   # The MIT License (MIT)
#
# Copyright (c) 2014 Richard Moore
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.


from .aes import AESBlockModeOfOperation, AESSegmentModeOfOperation, AESStreamModeOfOperation
from .util import append_PKCS7_padding, strip_PKCS7_padding, to_bufferable


# First we inject three functions to each of the modes of operations
#
#    _can_consume(size)
#       - Given a size, determine how many bytes could be consumed in
#         a single call to either the decrypt or encrypt method
#
#    _final_encrypt(data, padding = PADDING_DEFAULT)
#       - call and return encrypt on this (last) chunk of data,
#         padding as necessary; this will always be at least 16
#         bytes unless the total incoming input was less than 16
#         bytes
#
#    _final_decrypt(data, padding = PADDING_DEFAULT)
#       - same as _final_encrypt except for decrypt, for
#         stripping off padding
#

PADDING_NONE       = 'none'
PADDING_DEFAULT    = 'default'

# @TODO: Ciphertext stealing and explicit PKCS#7
# PADDING_CIPHERTEXT_STEALING
# PADDING_PKCS7

# ECB and CBC are block-only ciphers

def _block_can_consume(self, size):
    if size >= 16: return 16
    return 0

# After padding, we may have more than one block
def _block_final_encrypt(self, data, padding = PADDING_DEFAULT):
    if padding == PADDING_DEFAULT:
        data = append_PKCS7_padding(data)

    elif padding == PADDING_NONE:
        if len(data) != 16:
            raise Exception('invalid data length for final block')
    else:
        raise Exception('invalid padding option')

    if len(data) == 32:
        return self.encrypt(data[:16]) + self.encrypt(data[16:])

    return self.encrypt(data)


def _block_final_decrypt(self, data, padding = PADDING_DEFAULT):
    if padding == PADDING_DEFAULT:
        return strip_PKCS7_padding(self.decrypt(data))

    if padding == PADDING_NONE:
        if len(data) != 16:
            raise Exception('invalid data length for final block')
        return self.decrypt(data)

    raise Exception('invalid padding option')

AESBlockModeOfOperation._can_consume = _block_can_consume
AESBlockModeOfOperation._final_encrypt = _block_final_encrypt
AESBlockModeOfOperation._final_decrypt = _block_final_decrypt



# CFB is a segment cipher

def _segment_can_consume(self, size):
    return self.segment_bytes * int(size // self.segment_bytes)

# CFB can handle a non-segment-sized block at the end using the remaining cipherblock
def _segment_final_encrypt(self, data, padding = PADDING_DEFAULT):
    if padding != PADDING_DEFAULT:
        raise Exception('invalid padding option')

    faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes)))
    padded = data + to_bufferable(faux_padding)
    return self.encrypt(padded)[:len(data)]

# CFB can handle a non-segment-sized block at the end using the remaining cipherblock
def _segment_final_decrypt(self, data, padding = PADDING_DEFAULT):
    if padding != PADDING_DEFAULT:
        raise Exception('invalid padding option')

    faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes)))
    padded = data + to_bufferable(faux_padding)
    return self.decrypt(padded)[:len(data)]

AESSegmentModeOfOperation._can_consume = _segment_can_consume
AESSegmentModeOfOperation._final_encrypt = _segment_final_encrypt
AESSegmentModeOfOperation._final_decrypt = _segment_final_decrypt



# OFB and CTR are stream ciphers

def _stream_can_consume(self, size):
    return size

def _stream_final_encrypt(self, data, padding = PADDING_DEFAULT):
    if padding not in [PADDING_NONE, PADDING_DEFAULT]:
        raise Exception('invalid padding option')

    return self.encrypt(data)

def _stream_final_decrypt(self, data, padding = PADDING_DEFAULT):
    if padding not in [PADDING_NONE, PADDING_DEFAULT]:
        raise Exception('invalid padding option')

    return self.decrypt(data)

AESStreamModeOfOperation._can_consume = _stream_can_consume
AESStreamModeOfOperation._final_encrypt = _stream_final_encrypt
AESStreamModeOfOperation._final_decrypt = _stream_final_decrypt



class BlockFeeder(object):
    '''The super-class for objects to handle chunking a stream of bytes
       into the appropriate block size for the underlying mode of operation
       and applying (or stripping) padding, as necessary.'''

    def __init__(self, mode, feed, final, padding = PADDING_DEFAULT):
        self._mode = mode
        self._feed = feed
        self._final = final
        self._buffer = to_bufferable("")
        self._padding = padding

    def feed(self, data = None):
        '''Provide bytes to encrypt (or decrypt), returning any bytes
           possible from this or any previous calls to feed.

           Call with None or an empty string to flush the mode of
           operation and return any final bytes; no further calls to
           feed may be made.'''

        if self._buffer is None:
            raise ValueError('already finished feeder')

        # Finalize; process the spare bytes we were keeping
        if not data:
            result = self._final(self._buffer, self._padding)
            self._buffer = None
            return result

        self._buffer += to_bufferable(data)

        # We keep 16 bytes around so we can determine padding
        result = to_bufferable('')
        while len(self._buffer) > 16:
            can_consume = self._mode._can_consume(len(self._buffer) - 16)
            if can_consume == 0: break
            result += self._feed(self._buffer[:can_consume])
            self._buffer = self._buffer[can_consume:]

        return result


class Encrypter(BlockFeeder):
    'Accepts bytes of plaintext and returns encrypted ciphertext.'

    def __init__(self, mode, padding = PADDING_DEFAULT):
        BlockFeeder.__init__(self, mode, mode.encrypt, mode._final_encrypt, padding)


class Decrypter(BlockFeeder):
    'Accepts bytes of ciphertext and returns decrypted plaintext.'

    def __init__(self, mode, padding = PADDING_DEFAULT):
        BlockFeeder.__init__(self, mode, mode.decrypt, mode._final_decrypt, padding)


# 8kb blocks
BLOCK_SIZE = (1 << 13)

def _feed_stream(feeder, in_stream, out_stream, block_size = BLOCK_SIZE):
    'Uses feeder to read and convert from in_stream and write to out_stream.'

    while True:
        chunk = in_stream.read(block_size)
        if not chunk:
            break
        converted = feeder.feed(chunk)
        out_stream.write(converted)
    converted = feeder.feed()
    out_stream.write(converted)


def encrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT):
    'Encrypts a stream of bytes from in_stream to out_stream using mode.'

    encrypter = Encrypter(mode, padding = padding)
    _feed_stream(encrypter, in_stream, out_stream, block_size)


def decrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT):
    'Decrypts a stream of bytes from in_stream to out_stream using mode.'

    decrypter = Decrypter(mode, padding = padding)
    _feed_stream(decrypter, in_stream, out_stream, block_size)
 0707010005c909000081b40000000000000000000000015ab2175400000802000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/pyaes/util.py  # The MIT License (MIT)
#
# Copyright (c) 2014 Richard Moore
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

# Why to_bufferable?
# Python 3 is very different from Python 2.x when it comes to strings of text
# and strings of bytes; in Python 3, strings of bytes do not exist, instead to
# represent arbitrary binary data, we must use the "bytes" object. This method
# ensures the object behaves as we need it to.

def to_bufferable(binary):
    return binary

def _get_byte(c):
    return ord(c)

try:
    xrange
except:

    def to_bufferable(binary):
        if isinstance(binary, bytes):
            return binary
        return bytes(ord(b) for b in binary)

    def _get_byte(c):
        return c

def append_PKCS7_padding(data):
    pad = 16 - (len(data) % 16)
    return data + to_bufferable(chr(pad) * pad)

def strip_PKCS7_padding(data):
    if len(data) % 16 != 0:
        raise ValueError("invalid length")

    pad = _get_byte(data[-1])

    if pad > 16:
        raise ValueError("invalid padding byte")

    return data[:-pad]
  0707010005c90a000081b40000000000000000000000015ab217540000005f000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/rcAddService.py    import logging

def addservice():
    log = logging.getLogger('CREATE')
    log.info('enter')

 0707010005c90b000081b40000000000000000000000015ab21754000018f5000000660000000000000000000000000000002600000000root/usr/share/opensvc/lib/rcAdvfs.py import os
import glob
from rcUtilities import call

class ExInit(Exception):
    pass

class Fset(object):
    """
dor
	Id           : 46a70bfd.000964b0.6.8001
	Files        :      158,  SLim=        0,  HLim=        0
	Blocks  (1k) : 36035624,  SLim=        0,  HLim=        0
	Quota Status : user=on  group=on
	Object Safety: off
	Fragging     : on
	DMAPI        : off
stock_systemes
	Id           : 4ad8612f.000923f8.1.8001
	Clone is     : stock_systemes@osvc_sync
	Files        :   499709,  SLim=        0,  HLim=        0
	Blocks  (1k) : 35305996,  SLim=        0,  HLim=        0
	Quota Status : user=off group=off
	Object Safety: off
	Fragging     : on
	DMAPI        : off
stock_systemes@osvc_sync
	Id           : 4ad8612f.000923f8.2.8001
	Clone of     : stock_systemes
	Revision     : 1
	Object Safety: off
	Fragging     : on
	DMAPI        : off
    """
    def __init__(self, lines):
        for line in lines:
            if not line.startswith('\t'):
                self.name = line.strip()
            elif "Id" in line:
                self.fsetid = line.split(':')[-1].strip()
            elif "Clone of" in line:
                self.cloneof = line.split(':')[-1].strip()
            elif "Clone is" in line:
                self.cloneis = line.split(':')[-1].strip()
            elif "Revision" in line:
                self.revision = line.split(':')[-1].strip()
            elif "Files" in line:
                line = line[line.index(':')+1:]
                l = line.split()
                self.files_count = int(l[0].replace(',',''))
                self.files_slim = int(l[2].replace(',',''))
                self.files_hlim = int(l[4].replace(',',''))
            elif "Blocks" in line:
                line = line[line.index(':')+1:]
                l = line.split()
                self.block_count = int(l[0].replace(',',''))
                self.block_slim = int(l[2].replace(',',''))
                self.block_hlim = int(l[4].replace(',',''))

    def fsname(self):
        return "#".join((self.domain.name, self.name))

    def __str__(self):
        s = "fileset:\n"
        s += " fsetid: %s\n" % self.fsetid
        s += " name: %s\n" % self.name
        s += " fsname: %s\n" % self.fsname()
        s += " files_count: %d\n" % self.files_count
        s += " files_slim: %d\n" % self.files_slim
        s += " files_hlim: %d\n" % self.files_hlim
        s += " block_count: %d\n" % self.block_count
        s += " block_slim: %d\n" % self.block_slim
        s += " block_hlim: %d\n" % self.block_hlim
        return s

class Volume(object):
    def __init__(self, s):
        l = s.split()
        if len(l) != 8:
            raise ExInit()
        self.volid = l[0]
        self.size = int(l[1])
        self.free = int(l[2])
        self.used_pct = int(l[3].replace('%',''))
        self.cmode = l[4]
        self.rblks = int(l[5])
        self.wblks = int(l[6])
        self.name = l[7]

    def __str__(self):
        s = "volume:\n"
        s += " volid: %s\n" % self.volid
        s += " name: %s\n" % self.name
        s += " size: %s\n" % self.size
        s += " free: %s\n" % self.free
        s += " used_pct: %s\n" % self.used_pct
        s += " cmode: %s\n" % self.cmode
        s += " rblks: %s\n" % self.rblks
        s += " wblks: %s\n" % self.wblks
        return s

class Fdmn(object):
    def __init__(self, name):
        self.used_pct = 0
        self.size = 0
        self.free = 0

        cmd = ['showfdmn', name]
        out, err, ret = call(cmd)
        if ret != 0:
            raise ExInit()
        d = {}
        """
               Id	       Date Created  LogPgs  Version  Domain Name
46a70bfd.000964b0  Wed Jul 25 10:38:21 2007     512        4  dom1

  Vol    1K-Blks        Free  % Used  Cmode  Rblks  Wblks  Vol Name
   2L   62914560    21056568     67%     on    256    256  /dev/disk/dsk13c
        """
        lines = out.split('\n')
        if len(lines) < 5:
            raise ExInit()
        header = lines[2].split()
        self.domid = header[0]
        self.name = header[-1]
        self.version = header[-2]
        self.logpgs = header[-3]
        self.vols = {}
        self.fsets = {}
        for line in lines[5:]:
            try:
                v = Volume(line)
                self += v
            except ExInit:
                pass

        cmd = ['showfsets', '-k', name]
        out, err, ret = call(cmd)
        if ret != 0:
            raise ExInit()
        lines = out.split('\n')
        n_lines = len(lines)
        if n_lines == 0:
            return
        for i, line in enumerate(lines):
            if i != 0 and not line.startswith('\t') or i == n_lines - 1:
                f = Fset(lines[h:i])
                self += f
            if not line.startswith('\t'):
                h = i

    def __iadd__(self, o):
        if type(o) == Volume:
            self.size += o.size
            self.free += o.free
            self.used_pct = int(100. * (self.size - self.free) / self.size)
            o.domain = self
            self.vols[o.name] = o
        elif type(o) == Fset:
            o.domain = self
            self.fsets[o.name] = o
        return self

    def __str__(self):
        s = "domain:\n"
        s += " domid: %s\n" % self.domid
        s += " name: %s\n" % self.name
        s += " version: %s\n" % self.version
        s += " logpgs: %s\n" % self.logpgs
        for v in self.vols.values():
            s += str(v)
        for v in self.fsets.values():
            s += str(v)
        return s

    def list_volnames(self):
        l = []
        for v in self.vols.values():
            l.append(v.name)
        return l

class Fdmns(object):
    def __init__(self):
        self.load_fdmns()

    def list_fdmns(self):
        return self.fdmns.keys()

    def load_fdmns(self):
        self.fdmns = {}
        for n in glob.glob('/etc/fdmns/*'):
            n = os.path.basename(n)
            if n.startswith('.'):
                continue
            self.fdmns[n] = {}

    def load_fdmn(self, name):
        d = Fdmn(name)
        self.fdmns[name] = d

    def get_fdmn(self, name):
        if name not in self.fdmns:
            return
        if len(self.fdmns[name]) == 0:
            self.load_fdmn(name)
        return self.fdmns[name]

if __name__ == "__main__":
    o = Fdmns()
    print(o.list_fdmns())
    d = o.get_fdmn('dom1')
    print(d)
   0707010005c90c000081b40000000000000000000000015ab21754000005c0000000660000000000000000000000000000002700000000root/usr/share/opensvc/lib/rcAmazon.py    import json
from rcUtilities import which, justcall
import rcExceptions as ex

class Amazon(object):
    instance_id = None
    instance_data = None

    def aws(self, cmd, verbose=True):
        if hasattr(self.svc, "aws") and which(self.svc.aws) is not None:
            _cmd = [self.svc.aws]
        else:
            _cmd = ["aws"]
        _cmd += ["--output=json"]
        if hasattr(self.svc, "aws_profile"):
            _cmd += ["--profile", self.svc.aws_profile]
        _cmd += cmd
        if verbose:
            self.log.info(" ".join(_cmd))
        out, err, ret = justcall(_cmd)
        if ret != 0:
            raise ex.excError(err)
        data = json.loads(out)
        return data

    def get_instance_id(self):
        if self.instance_id is not None:
            return self.instance_id
        import httplib
        c = httplib.HTTPConnection("instance-data")
        c.request("GET", "/latest/meta-data/instance-id")
        self.instance_id = c.getresponse().read()
        return self.instance_id

    def get_instance_data(self, refresh=False):
        if self.instance_data is not None and not refresh:
            return self.instance_data
        data = self.aws(["ec2", "describe-instances", "--instance-ids", self.get_instance_id()], verbose=False)
        try:
            self.instance_data = data["Reservations"][0]["Instances"][0]
        except Exception as e:
            self.instance_data = None
        return self.instance_data


0707010005c90d000081b40000000000000000000000015ab21754000052bb000000660000000000000000000000000000002600000000root/usr/share/opensvc/lib/rcAsset.py import os
import datetime
from subprocess import *

import rcExceptions as ex
from rcGlobalEnv import rcEnv
from rcUtilities import try_decode, justcall, which
from converters import print_size

class Asset(object):
    s_config = "config"
    s_probe = "probe"
    s_default = "default"

    def __init__(self, node):
        self.node = node

    def get_mem_bytes(self):
        s = '0'
        source = self.s_default
        try:
            s = self.node.config.get('node', 'mem_bytes')
            source = self.s_config
        except:
            try:
                s = self._get_mem_bytes()
                source = self.s_probe
            except:
                pass
        return {
            "title": "mem",
            "value": s,
            "source": source,
            "formatted_value": print_size(s)
        }

    def get_mem_banks(self):
        s = '0'
        source = self.s_default
        try:
            s = self.node.config.get('node', 'mem_banks')
            source = self.s_config
        except:
            try:
                s = self._get_mem_banks()
                source = self.s_probe
            except:
                pass
        return {
            "title": "mem banks",
            "value": s,
            "source": source
        }

    def get_mem_slots(self):
        s = '0'
        source = self.s_default
        try:
            s = self.node.config.get('node', 'mem_slots')
            source = self.s_config
        except:
            try:
                s = self._get_mem_slots()
                source = self.s_probe
            except:
                pass
        return {
            "title": "mem slots",
            "value": s,
            "source": source
        }

    def get_os_vendor(self):
        s = 'Unknown'
        source = self.s_default
        try:
            s = self.node.config.get('node', 'os_vendor')
            source = self.s_config
        except:
            try:
                s = self._get_os_vendor()
                source = self.s_probe
            except:
                pass
        return {
            "title": "os vendor",
            "value": s,
            "source": source
        }

    def get_os_release(self):
        s = 'Unknown'
        source = self.s_default
        try:
            s = self.node.config.get('node', 'os_release')
            source = self.s_config
        except:
            try:
                s = self._get_os_release()
                source = self.s_probe
            except:
                pass
        return {
            "title": "os release",
            "value": s,
            "source": source
        }

    def get_os_kernel(self):
        s = 'Unknown'
        source = self.s_default
        try:
            s = self.node.config.get('node', 'os_kernel')
            source = self.s_config
        except:
            try:
                s = self._get_os_kernel()
                source = self.s_probe
            except:
                pass
        return {
            "title": "os kernel",
            "value": s,
            "source": source
        }

    def get_os_arch(self):
        s = 'Unknown'
        source = self.s_default
        try:
            s = self.node.config.get('node', 'os_arch')
            source = self.s_config
        except:
            try:
                s = self._get_os_arch()
                source = self.s_probe
            except:
                pass
        return {
            "title": "os arch",
            "value": s,
            "source": source
        }

    def get_cpu_freq(self):
        s = '0'
        source = self.s_default
        try:
            s = self.node.config.get('node', 'cpu_freq')
            source = self.s_config
        except:
            try:
                s = self._get_cpu_freq()
                source = self.s_probe
            except:
                pass
        return {
            "title": "cpu freq",
            "value": s,
            "source": source
        }

    def get_cpu_threads(self):
        s = '0'
        source = self.s_default
        try:
            s = self.node.config.get('node', 'cpu_threads')
            source = self.s_config
        except:
            try:
                s = self._get_cpu_threads()
                source = self.s_probe
            except:
                pass
        return {
            "title": "cpu threads",
            "value": s,
            "source": source
        }

    def get_cpu_cores(self):
        s = '0'
        source = self.s_default
        try:
            s = self.node.config.get('node', 'cpu_cores')
            source = self.s_config
        except:
            try:
                s = self._get_cpu_cores()
                source = self.s_probe
            except:
                pass
        return {
            "title": "cpu cores",
            "value": s,
            "source": source
        }

    def get_cpu_dies(self):
        s = '0'
        source = self.s_default
        try:
            s = self.node.config.get('node', 'cpu_dies')
            source = self.s_config
        except:
            try:
                s = self._get_cpu_dies()
                source = self.s_probe
            except:
                pass
        return {
            "title": "cpu dies",
            "value": s,
            "source": source
        }

    def get_cpu_model(self):
        s = 'Unknown'
        source = self.s_default
        try:
            s = self.node.config.get('node', 'cpu_model')
            source = self.s_config
        except:
            try:
                s = self._get_cpu_model()
                source = self.s_probe
            except:
                pass
        return {
            "title": "cpu model",
            "value": s,
            "source": source
        }

    def get_serial(self):
        s = 'Unknown'
        source = self.s_default
        try:
            s = self.node.config.get('node', 'serial')
            source = self.s_config
        except:
            try:
                s = self._get_serial()
                source = self.s_probe
            except:
                pass
        return {
            "title": "serial",
            "value": s,
            "source": source
        }

    def get_bios_version(self):
        s = ''
        source = self.s_default
        try:
            s = self.node.config.get('node', 'bios_version')
            source = self.s_config
        except:
            try:
                s = self._get_bios_version()
                source = self.s_probe
            except:
                pass
        return {
            "title": "bios version",
            "value": s,
            "source": source
        }

    def get_sp_version(self):
        s = ''
        source = self.s_default
        try:
            s = self.node.config.get('node', 'sp_version')
            source = self.s_config
        except:
            try:
                s = self._get_sp_version()
                source = self.s_probe
            except:
                pass
        return {
            "title": "sp version",
            "value": s,
            "source": source
        }

    def get_enclosure(self):
        s = 'Unknown'
        source = self.s_default
        try:
            s = self.node.config.get('node', 'enclosure')
            source = self.s_config
        except:
            try:
                s = self._get_enclosure()
                source = self.s_probe
            except:
                pass
        return {
            "title": "enclosure",
            "value": s,
            "source": source
        }

    def get_tz(self):
        s = None
        source = self.s_default
        try:
            s = self.node.config.get('node', 'tz')
            source = self.s_config
        except:
            try:
                s = self._get_tz()
                source = self.s_probe
            except Exception as e:
                pass
        return {
            "title": "timezone",
            "value": s,
            "source": source
        }

    def _get_tz(self):
        cmd = ["date", "+%z"]
        out, err, ret = justcall(cmd)
        out = out.strip()
        if len(out) != 5:
            return
        return out[:3] + ":" + out[3:]

    def get_connect_to(self):
        s = None
        source = self.s_default
        try:
            s = self.node.config.get('node', 'connect_to')
            source = self.s_config
        except:
            try:
                s = self._get_connect_to()
                source = self.s_probe
            except:
                pass
        return {
            "title": "listener address",
            "value": s,
            "source": source
        }

    def _get_connect_to(self):
        if self.data["model"]["value"] != "Google":
            return
        if not which("gcloud"):
            return
        cmd = ["gcloud", "compute", "instances", "describe", "-q", "--format", "json", rcEnv.nodename]
        out, err, ret = justcall(cmd)
        return self._parse_connect_to(out)

    def _parse_connect_to(self, out):
        """
	  "networkInterfaces": [
	    {
	      "accessConfigs": [
		{
		  "kind": "compute#accessConfig",
		  "name": "external-nat",
		  "natIP": "23.251.137.71",
		  "type": "ONE_TO_ONE_NAT"
		}
	      ],
	      "name": "nic0",
	      "networkIP": "10.132.0.2",
	    }
        """
        import json
        try:
            data = json.loads(out)
        except:
            return
        nics = [d for d in data["networkInterfaces"] if len(d["accessConfigs"]) > 0]
        if len(nics) == 0:
            return
        for nic in nics:
            if nic["name"] == "nic0":
                return nic["accessConfigs"][0]["natIP"]
        return nics[0]["accessConfigs"][0]["natIP"]

    def get_manufacturer(self):
        s = 'Unknown'
        source = self.s_default
        try:
            s = self.node.config.get('node', 'manufacturer')
            source = self.s_config
        except:
            try:
                s = self._get_manufacturer()
                source = self.s_probe
            except:
                pass
        return {
            "title": "manufacturer",
            "value": s,
            "source": source
        }

    def get_model(self):
        s = 'Unknown'
        source = self.s_default
        try:
            s = self.node.config.get('node', 'model')
            source = self.s_config
        except:
            try:
                s = self._get_model()
                source = self.s_probe
            except:
                pass
        return {
            "title": "model",
            "value": s,
            "source": source
        }

    def get_version(self):
        s = self.node.agent_version()
        return {
            "title": "agent version",
            "value": s,
            "source": self.s_probe
        }

    def get_listener_port(self):
        s = str(rcEnv.listener_port)
        source = self.s_default
        try:
            s = str(self.node.config.getint('listener', 'port'))
            source = self.s_config
        except:
            pass
        return {
            "title": "listener port",
            "value": s,
            "source": source
        }

    def get_from_conf(self, section, kw, title):
        try:
            s = self.node.conf_get(section, kw)
            source = self.s_config
        except ex.OptNotFound as exc:
            s = exc.default
            source = self.s_default
        return {
            "title": title,
            "value": s,
            "source": source
        }

    def get_node_env(self):
        return self.get_from_conf("node", "env", "environment")

    def get_sec_zone(self):
        return self.get_from_conf("node", "sec_zone", "security zone")

    def get_asset_env(self):
        return self.get_from_conf("node", "asset_env", "asset environment")

    def get_loc_country(self):
        return self.get_from_conf("node", "loc_country", "loc, country")

    def get_loc_city(self):
        return self.get_from_conf("node", "loc_city", "loc, city")

    def get_loc_addr(self):
        return self.get_from_conf("node", "loc_addr", "loc, addr")

    def get_loc_building(self):
        return self.get_from_conf("node", "loc_building", "loc, building")

    def get_loc_floor(self):
        return self.get_from_conf("node", "loc_floor", "loc, floor")

    def get_loc_room(self):
        return self.get_from_conf("node", "loc_room", "loc, room")

    def get_loc_rack(self):
        return self.get_from_conf("node", "loc_rack", "loc, rack")

    def get_loc_zip(self):
        return self.get_from_conf("node", "loc_zip", "loc, zip")

    def get_team_integ(self):
        return self.get_from_conf("node", "team_integ", "team, integ")

    def get_team_support(self):
        return self.get_from_conf("node", "team_support", "team, support")

    def get_hba(self):
        try:
            hba = self._get_hba()
        except:
            hba = []
        return hba

    def get_targets(self):
        try:
            s = self._get_targets()
        except:
            s = []
        return s

    def get_hardware(self):
        try:
            s = self._get_hardware()
        except:
            s = []
        return s

    def get_uids(self):
        return self.get_ids("/etc/passwd", ("username", "uid"))

    def get_gids(self):
        return self.get_ids("/etc/group", ("groupname", "gid"))

    def get_ids(self, p, keys):
        if rcEnv.sysname == "Windows":
            return []
        if not os.path.exists(p):
            return []
        with open(p, 'r') as f:
            buff = f.read()
        d = []
        for line in buff.split('\n'):
            line = line.strip()
            if line.startswith("#"):
                continue
            l = line.split(':')
            if len(l) < 3:
                continue
            try:
                i = int(l[2])
            except:
                continue
            name = repr(l[0]).strip("'")
            d.append({
                keys[0]: name,
                keys[1]: l[2]
            })
        return d

    def get_lan(self):
        kwargs = {'mcast': True}
        if rcEnv.sysname == 'HP-UX':
            kwargs['hwaddr'] = True
        rcIfconfig = __import__('rcIfconfig'+rcEnv.sysname)
        ifconfig = rcIfconfig.ifconfig(**kwargs)
        lan = {}
        for intf in ifconfig.intf:
            if len(intf.hwaddr) == 0:
                continue
            if intf.hwaddr not in lan:
                lan[intf.hwaddr] = []
            if type(intf.ipaddr) == str and intf.ipaddr != '':
                d = {'type': 'ipv4',
                     'intf': intf.name,
                     'addr': intf.ipaddr,
                     'mask': intf.mask,
                     'flag_deprecated': intf.flag_deprecated,
                    }
                lan[intf.hwaddr] += [d]
            elif type(intf.ipaddr) == list:
                for i, ip in enumerate(intf.ipaddr):
                    if ip != '':
                        d = {'type': 'ipv4',
                             'intf': intf.name,
                             'addr': ip,
                             'mask': intf.mask[i],
                             'flag_deprecated': intf.flag_deprecated,
                            }
                    lan[intf.hwaddr] += [d]
            for i, ip6 in enumerate(intf.ip6addr):
                d = {'type': 'ipv6',
                     'intf': intf.name,
                     'addr': intf.ip6addr[i],
                     'mask': intf.ip6mask[i],
                     'flag_deprecated': intf.flag_deprecated,
                    }
                lan[intf.hwaddr] += [d]
            if intf.name in ifconfig.mcast_data:
                for addr in ifconfig.mcast_data[intf.name]:
                    if ':' in addr:
                        addr_type = 'ipv6'
                    else:
                        addr_type = 'ipv4'
                    d = {'type': addr_type,
                         'intf': intf.name,
                         'addr': addr,
                         'mask': "",
                         'flag_deprecated': intf.flag_deprecated,
                        }
                    lan[intf.hwaddr] += [d]

        return lan

    def get_last_boot(self):
        os.environ["LANG"] = "C"
        cmd = ["/usr/bin/uptime"]
        p = Popen(cmd, stdout=PIPE)
        out, err = p.communicate()
        l = out.split()

        i = 0
        for s in ("days,", "day(s),"):
            try:
                i = l.index(s)
                break
            except:
                pass

        if i == 0:
            last = datetime.datetime.now()
        else:
            try:
                last = datetime.datetime.now() - datetime.timedelta(days=int(l[i-1]))
            except:
                return

        last = last.strftime("%Y-%m-%d")
        return {
            "title": "last boot",
            "value": last,
            "source": self.s_probe
        }

    def get_asset_dict(self):
        self.data = {}
        self.data['nodename'] = {
            "title": "nodename",
            "value": rcEnv.nodename,
            "source": self.s_probe
        }
        self.data['fqdn'] = {
            "title": "fqdn",
            "value": rcEnv.fqdn,
            "source": self.s_probe
        }
        self.data['version'] = self.get_version()
        self.data['os_name'] = {
            "title": "os name",
            "value": rcEnv.sysname,
            "source": self.s_probe
        }
        self.data['os_vendor'] = self.get_os_vendor()
        self.data['os_release'] = self.get_os_release()
        self.data['os_kernel'] = self.get_os_kernel()
        self.data['os_arch'] = self.get_os_arch()
        self.data['mem_bytes'] = self.get_mem_bytes()
        self.data['mem_banks'] = self.get_mem_banks()
        self.data['mem_slots'] = self.get_mem_slots()
        self.data['cpu_freq'] = self.get_cpu_freq()
        self.data['cpu_threads'] = self.get_cpu_threads()
        self.data['cpu_cores'] = self.get_cpu_cores()
        self.data['cpu_dies'] = self.get_cpu_dies()
        self.data['cpu_model'] = self.get_cpu_model()
        self.data['serial'] = self.get_serial()
        self.data['model'] = self.get_model()
        self.data['manufacturer'] = self.get_manufacturer()
        self.data['bios_version'] = self.get_bios_version()
        self.data['sp_version'] = self.get_sp_version()
        self.data['node_env'] = self.get_node_env()
        self.data['enclosure'] = self.get_enclosure()
        self.data['listener_port'] = self.get_listener_port()
        connect_to = self.get_connect_to()
        if connect_to is not None:
            self.data['connect_to'] = connect_to
        last_boot = self.get_last_boot()
        if last_boot is not None:
            self.data['last_boot'] = last_boot
        sec_zone = self.get_sec_zone()
        if sec_zone is not None:
            self.data['sec_zone'] = sec_zone
        asset_env = self.get_asset_env()
        if asset_env is not None:
            self.data['asset_env'] = asset_env
        tz = self.get_tz()
        if tz is not None:
            self.data['tz'] = tz
        loc_country = self.get_loc_country()
        if loc_country is not None:
            self.data['loc_country'] = loc_country
        loc_city = self.get_loc_city()
        if loc_city is not None:
            self.data['loc_city'] = loc_city
        loc_building = self.get_loc_building()
        if loc_building is not None:
            self.data['loc_building'] = loc_building
        loc_room = self.get_loc_room()
        if loc_room is not None:
            self.data['loc_room'] = loc_room
        loc_rack = self.get_loc_rack()
        if loc_rack is not None:
            self.data['loc_rack'] = loc_rack
        loc_addr = self.get_loc_addr()
        if loc_addr is not None:
            self.data['loc_addr'] = loc_addr
        loc_floor = self.get_loc_floor()
        if loc_floor is not None:
            self.data['loc_floor'] = loc_floor
        loc_zip = self.get_loc_zip()
        if loc_zip is not None:
            self.data['loc_zip'] = loc_zip
        team_integ = self.get_team_integ()
        if team_integ is not None:
            self.data['team_integ'] = team_integ
        team_support = self.get_team_support()
        if team_support is not None:
            self.data['team_support'] = team_support
        hardware = self.get_hardware()
        if hardware is not None:
            self.data['hardware'] = hardware
        hba = self.get_hba()
        if hba is not None:
            self.data['hba'] = hba
        targets = self.get_targets()
        if targets is not None:
            self.data['targets'] = targets
        lan = self.get_lan()
        if lan is not None:
            self.data['lan'] = lan
        uids = self.get_uids()
        if uids is not None:
            self.data['uids'] = uids
        gids = self.get_gids()
        if gids is not None:
            self.data['gids'] = gids
        return self.data
 0707010005c90e000081b40000000000000000000000015ab2175400000c3a000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/rcAssetAIX.py  import os
import datetime
from rcUtilities import justcall, which
from rcGlobalEnv import rcEnv
import rcAsset

class Asset(rcAsset.Asset):
    def __init__(self, node):
        rcAsset.Asset.__init__(self, node)
        (out, err, ret) = justcall(['prtconf'])
        if ret != 0:
            self.prtconf = []
        else:
            self.prtconf = out.split('\n')
        self.lpar = self.is_lpar()

    def is_lpar(self):
        cmd = ["prtconf", "-L"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise
        if '-1' in out:
            return False
        return True

    def _get_mem_bytes(self):
        cmd = ["prtconf", "-m"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return '0'
        l = out.split()
        if 'Memory Size:' not in out:
            return '0'
        if len(l) != 4:
            return '0'

        size = int(l[2])
        unit = l[3]

        if unit == 'GB':
            size = size * 1024
        elif unit == 'MB':
            pass
        else:
            return '0'

        return str(size)

    def _get_mem_banks(self):
        if self.lpar:
            return '0'
        return 'TODO'

    def _get_mem_slots(self):
        if self.lpar:
            return '0'
        return 'TODO'

    def _get_os_vendor(self):
        return 'IBM'

    def _get_os_name(self):
        return 'AIX'

    def _get_os_release(self):
        cmd = ["oslevel", "-s"]
        (out, err, ret) = justcall(cmd)
        if ret != 0:
            return 'Unknown'
        return out.strip()

    def _get_os_kernel(self):
        cmd = ["oslevel", "-r"]
        (out, err, ret) = justcall(cmd)
        if ret != 0:
            return 'Unknown'
        return out.strip()

    def _get_os_arch(self):
        for line in self.prtconf:
            if "Kernel Type:" in line:
                return line.split(":")[-1].strip()
        return 'Unknown'

    def _get_cpu_freq(self):
        for line in self.prtconf:
            if "Processor Clock Speed:" in line:
                return line.split(":")[-1].split()[0].strip()
        return '0'

    def _get_cpu_cores(self):
        cmd = ["bindprocessor", "-q"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return '0'
        l = out.split(":")
        return str(len(l[-1].strip().split()))

    def _get_cpu_dies(self):
        cmd = ["lsdev", "-Cc", "processor"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return '0'
        return str(len([line for line in out.split('\n') if 'proc' in line]))

    def _get_cpu_model(self):
        for line in self.prtconf:
            if "Processor Type:" in line:
                return line.split(":")[-1].strip()
        return 'Unknown'

    def _get_serial(self):
        for line in self.prtconf:
            if "Machine Serial Number:" in line:
                return line.split(":")[-1].strip()
        return 'Unknown'

    def _get_model(self):
        for line in self.prtconf:
            if "System Model:" in line:
                return line.split(":")[-1].strip()
        return 'Unknown'
  0707010005c90f000081b40000000000000000000000015ab2175400000c6a000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/rcAssetDarwin.py   import os
import datetime
from rcUtilities import justcall, which
from rcGlobalEnv import rcEnv
import rcAsset

class Asset(rcAsset.Asset):
    def __init__(self, node):
        rcAsset.Asset.__init__(self, node)
        self.info = {}
        (out, err, ret) = justcall(['system_profiler', 'SPHardwareDataType', 'SPSoftwareDataType'])
        if ret == 0:
            for line in out.split('\n'):
                l = line.split(':')
                if len(l) != 2: continue
                self.info[l[0].strip()] = l[1].strip()
        self.memslots = 0
        self.membanks = 0
        self._collect_memory_info()

    def _collect_memory_info(self):
        (out, err, ret) = justcall(['system_profiler', 'SPMemoryDataType'])
        if ret == 0:
            inBlock = False
            for line in out.split('\n'):
                line = line.strip()
                if not inBlock and line.startswith("BANK"):
                    inBlock = True
                    self.memslots += 1
                if inBlock and line.startswith("Status"):
                    l = line.split(':')
                    if 'OK' in l[1].strip():
                        self.membanks += 1
                    inBlock = False

    def _get_mem_bytes(self):
        if 'Memory' not in self.info:
            return '0'
        m = self.info['Memory'].split()
        size = int(m[0])
        unit = m[1]
        if unit == 'GB':
            size = size * 1024
        elif unit == 'MB':
            pass
        else:
            raise
        return str(size)

    def _get_mem_banks(self):
        return str(self.membanks)

    def _get_mem_slots(self):
        return str(self.memslots)

    def _get_os_vendor(self):
        return 'Apple'

    def _get_os_release(self):
        if 'System Version' in self.info:
            return self.info['System Version']
        (out, err, ret) = justcall(['uname', '-r'])
        if ret != 0:
            return 'Unknown'
        return out.split()[0]

    def _get_os_kernel(self):
        if 'Kernel Version' not in self.info:
            return '0'
        return self.info['Kernel Version']

    def _get_os_arch(self):
        cmd = ['uname', '-m']
        (out, err, ret) = justcall(cmd)
        if ret != 0:
            return 'Unknown'
        return out.split('\n')[0]

    def _get_cpu_freq(self):
        if 'Processor Speed' not in self.info:
            return '0'
        return self.info['Processor Speed']

    def _get_cpu_cores(self):
        if 'Total Number of Cores' not in self.info:
            return '0'
        return self.info['Total Number of Cores']

    def _get_cpu_dies(self):
        if 'Number of Processors' not in self.info:
            return '0'
        return self.info['Number of Processors']

    def _get_cpu_model(self):
        if 'Processor Name' not in self.info:
            return '0'
        return self.info['Processor Name']

    def _get_serial(self):
        if 'Hardware UUID' not in self.info:
            return '0'
        return self.info['Hardware UUID']

    def _get_model(self):
        if 'Model Name' not in self.info:
            return '0'
        return self.info['Model Name']

  0707010005c910000081b40000000000000000000000015ab2175400000830000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/rcAssetFreeBSD.py  import os
import datetime
from rcUtilities import justcall, which
from rcGlobalEnv import rcEnv
import rcAssetLinux

class Asset(rcAssetLinux.Asset):
    def _get_mem_bytes(self):
        cmd = ['sysctl', 'hw.realmem']
        (out, err, ret) = justcall(cmd)
        if ret != 0:
            return '0'
        lines = out.split('\n')
        if len(lines) < 1:
            return '0'
        line = lines[0].split()
        if len(line) < 2:
            return '0'
        mb = int(line[-1])
        return str(mb/1024/1024)

    def _get_os_vendor(self):
        return 'FreeBSD'

    def _get_os_release(self):
        return self._get_os_kernel()

    def _get_os_arch(self):
        cmd = ['sysctl', 'hw.machine_arch']
        (out, err, ret) = justcall(cmd)
        if ret != 0:
            return 'Unknown'
        lines = out.split('\n')
        if len(lines) < 1:
            return 'Unknown'
        line = lines[0].split()
        if len(line) < 2:
            return 'Unknown'
        return line[-1]

    def _get_cpu_model(self):
        cmd = ['sysctl', 'hw.model']
        (out, err, ret) = justcall(cmd)
        if ret != 0:
            return 'Unknown'
        lines = out.split('\n')
        if len(lines) < 1:
            return 'Unknown'
        line = lines[0].split()
        if len(line) < 2:
            return 'Unknown'
        return " ".join(line[1:])

    def _get_cpu_cores(self):
        cmd = ['sysctl', 'hw.ncpu']
        (out, err, ret) = justcall(cmd)
        if ret != 0:
            return 'Unknown'
        lines = out.split('\n')
        if len(lines) < 1:
            return 'Unknown'
        line = lines[0].split()
        if len(line) < 2:
            return 'Unknown'
        return line[-1]

    def _get_cpu_freq(self):
        cmd = ['sysctl', 'hw.clockrate']
        (out, err, ret) = justcall(cmd)
        if ret != 0:
            return 'Unknown'
        lines = out.split('\n')
        if len(lines) < 1:
            return 'Unknown'
        line = lines[0].split()
        if len(line) < 2:
            return 'Unknown'
        return line[-1]
0707010005c911000081b40000000000000000000000015ab2175400002104000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/rcAssetHP-UX.py    import os
from rcUtilities import justcall, which
from rcGlobalEnv import rcEnv
from subprocess import *
import rcAsset
import datetime

os.environ['PATH'] += ":/opt/ignite/bin:/opt/propplus/bin"

class Asset(rcAsset.Asset):
    def __init__(self, node):
        rcAsset.Asset.__init__(self, node)

        out, err, ret = justcall(['print_manifest'])
        if ret != 0:
            self.manifest = []
        else:
            self.manifest = out.split('\n')

        self.parse_memory()

    def _get_mem_bytes(self):
        cmd = ['swapinfo', '-Mq']
        (out, err, ret) = justcall(cmd)
        if ret != 0:
            return '0'
        return str(int(out)//1024)

    def parse_memory(self):
        self.banks =  0
        self.slots =  0

        cmd = ['cprop', '-summary', '-c', 'Memory']
        out, err, ret = justcall(cmd)

        if ret != 0:
            return '0'

        in_banks = True

        for line in out.split('\n'):
            if 'Empty Slots' in line:
                in_banks = False
            elif 'Instance' in line:
                self.slots += 1
                if in_banks:
                    self.banks += 1

    def _get_mem_banks(self):
        s = str(self.banks)
        if s == '0':
            s = self._get_mem_banks_ts99()
        return s

    def _get_mem_slots(self):
        return str(self.slots)

    def _get_os_vendor(self):
        return 'HP'

    def _get_os_release(self):
        (out, err, ret) = justcall(['uname', '-r'])
        if ret != 0:
            return 'Unknown'
        return out.split('\n')[0].strip()

    def _get_os_kernel(self):
        (out, err, ret) = justcall(['swlist', '-l', 'bundle', 'QPKBASE'])
        if ret != 0:
            return 'Unknown'
        for line in out.split('\n'):
            if 'QPKBASE' in line:
                return line.split()[1]
        return 'Unknown'

    def _get_os_arch(self):
        (out, err, ret) = justcall(['uname', '-m'])
        if ret != 0:
            return 'Unknown'
        return out.split('\n')[0].strip()

    def _get_cpu_freq(self):
        freq = self._get_cpu_freq_manifest()
        if freq == "Unknown":
            freq = self._get_cpu_freq_adb()
        return freq

    def _get_cpu_freq_manifest(self):
        m = self._get_cpu_model()
        if '(' not in m:
            return "Unknown"
        s = m.split('(')[-1]
        s = s.split(',')[0]
        freq, unit = s.split()
        if unit == 'GHz':
            try:
                freq = float(freq)
            except:
                return "Unknown"
            freq = str(int(freq * 1000))
        return freq

    def _get_cpu_freq_adb(self):
        process = Popen(['adb', '/stand/vmunix', '/dev/kmem'], stdin=PIPE, stdout=PIPE, stderr=None)
        (out, err) = process.communicate(input='itick_per_usec/2d')
        if process.returncode != 0:
            process = Popen(['adb', '-k', '/stand/vmunix', '/dev/mem'], stdin=PIPE, stdout=PIPE, stderr=None)
            (out, err) = process.communicate(input='itick_per_usec/D')
        if process.returncode != 0:
            return 'Unknown'
        lines = out.split('\n')
        if len(lines) < 2:
            return 'Unknown'
        return lines[1].split()[-1]

    def _get_cpu_cores(self):
        for line in self.manifest:
            if 'Processors:' in line:
                return line.split()[-1]
        return '0'

    def _get_cpu_dies(self):
        n = self._get_cpu_cores_per_die()
        if n == 0:
            return str(self._get_cpu_dies_ts99())
        cores = int(self._get_cpu_cores())
        return str(cores // n)

    def _get_cpu_cores_per_die(self):
        n = 0
        i = 0
        for line in self.manifest:
            line = line.replace('(', '').replace(')', '')
            if 'Processors:' in line:
                i = 1
                continue
            if i > 0 and i < 4:
                i += 1
                if "core" not in line and "socket" not in line:
                    continue
                words = line.split()
                for j, w in enumerate(words):
                    if w == "socket":
                        try:
                            n = int(words[j-2])
                        except:
                            break
                    if 'core' in w:
                        try:
                            n = int(words[j-1])
                        except:
                            break
            elif i >= 4:
                break
        return n

    def _get_mem_banks_ts99(self):
        p = '/var/tombstones/ts99'
        if not os.path.exists(p):
            return '0'
        with open(p, 'r') as f:
            buff = f.read()
        lines = buff.split('\n')
        c = 0
        for line in lines:
            if "DIMM Error Information" in line:
                c += 1
        return str(c)

    def _get_cpu_dies_ts99(self):
        # count different serial numbers
        p = '/var/tombstones/ts99'
        if not os.path.exists(p):
            return 1
        with open(p, 'r') as f:
            buff = f.read()
        lines = buff.split('\n')
        serials = set()
        for line in lines:
            if "Cpu Serial Number" in line:
                serials.add(line.split()[-1])
        if len(serials) == 0:
            return 1
        return len(serials)

    def _get_cpu_model(self):
        s = self._get_cpu_model_manifest()
        if s == 'Unknown':
            s = self._get_cpu_model_ts99()
        return s

    def _get_cpu_model_ts99(self):
        p = '/var/tombstones/ts99'
        if not os.path.exists(p):
            return 'Unknown'
        with open(p, 'r') as f:
            buff = f.read()
        lines = buff.split('\n')
        for line in lines:
            if "CPU Module" in line:
                return line.strip().replace("CPU Module", "rev").replace('  ', ' ')
        return 'Unknown'

    def _get_cpu_model_manifest(self):
        marker = False
        for line in self.manifest:
            if 'Processors:' in line:
                marker = True
                continue
            if marker:
                if "processor" not in line:
                    return 'Unknown'
                e = line.split()
                return ' '.join(e[1:]).replace('processors','').replace('processor','')
        return 'Unknown'

    def _get_serial(self):
        (out, err, ret) = justcall(['getconf', 'MACHINE_SERIAL'])
        if ret != 0:
            return 'Unknown'
        return out.strip()

    def _get_model(self):
        (out, err, ret) = justcall(['getconf', 'MACHINE_MODEL'])
        if ret != 0:
            return 'Unknown'
        return out.strip()

    def __get_hba(self):
        if hasattr(self, "hba"):
            return self.hba
        self.hba = []
        cmd = ['/usr/sbin/ioscan', '-FunC', 'fc']
        out, err, ret = justcall(cmd)
        if ret != 0:
            return self.hba
        lines = out.split('\n')
        if len(lines) < 2:
            return self.hba
        for line in lines:
            if '/dev/' not in line:
                continue
            dev = line.strip()
            hba_type = 'fc'

            cmd = ['/opt/fcms/bin/fcmsutil', dev]
            out, err, ret = justcall(cmd)
            if ret != 0:
                continue
            for _line in out.split('\n'):
                if not 'N_Port Port World Wide Name' in _line:
                    continue
                hba_id = _line.split('=')[-1].strip().lstrip("0x")

            cmd = ['/opt/fcms/bin/fcmsutil', dev, 'get', 'remote', 'all']
            out, err, ret = justcall(cmd)
            if ret != 0:
                continue
            targets = []
            for _line in out.split('\n'):
                if not 'Target Port World Wide Name' in _line:
                    continue
                targets.append(_line.split('=')[-1].strip().lstrip("0x"))

            self.hba.append((hba_id, hba_type, targets))
        return self.hba

    def _get_hba(self):
        hba = self.__get_hba()
        l = []
        for hba_id, hba_type, targets in hba:
            l.append((hba_id, hba_type))
        return [{"hba_id": e[0], "hba_type": e[1]} for e in l]

    def _get_targets(self):
        hba = self.__get_hba()
        l = []
        for hba_id, hba_type, targets in hba:
            for target in targets:
                l.append({"hba_id": hba_id, "tgt_id": target})
        return l

0707010005c912000081b40000000000000000000000015ab2175400005278000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/rcAssetLinux.py    import os
import re
import datetime
from rcUtilities import justcall, which, bdecode
import rcAsset

def is_container():
    p = '/proc/1/environ'
    if not os.path.exists(p):
        return False
    with open(p, 'r') as f:
        buff = f.read()
    if "container=lxc" in bdecode(buff):
        return True
    return False

class Asset(rcAsset.Asset):
    def __init__(self, node):
        rcAsset.Asset.__init__(self, node)
        self.container = is_container()
        self.detect_xen()
        if self.container:
            self.dmidecode = []
        else:
            out, err, ret = justcall(['dmidecode'])
            if ret != 0:
                self.dmidecode = []
            else:
                self.dmidecode = out.split('\n')

    def _get_mem_bytes_esx(self):
        cmd = ['vmware-cmd', '-s', 'getresource', 'system.mem.totalMem']
        out, err, ret = justcall(cmd)
        if ret != 0:
            return '0'
        l = out.split(' = ')
        if len(l) < 2:
            return '0'
        try:
            size = str(int(l[-1])/1024)
        except:
            size = '0'
        return size

    def _get_mem_bytes_hv(self):
        if which('virsh'):
            return self._get_mem_bytes_virsh()
        if which('xm'):
            return self._get_mem_bytes_xm()
        else:
            return '0'

    def _get_mem_bytes_virsh(self):
        cmd = ['virsh', 'nodeinfo']
        out, err, ret = justcall(cmd)
        if ret != 0:
            return '0'
        lines = out.split('\n')
        for line in lines:
            if 'Memory size' not in line:
                continue
            l = line.split()
            if len(l) < 2:
                continue
            return l[-2]
        return '0'

    def _get_mem_bytes_xm(self):
        cmd = ['xm', 'info']
        out, err, ret = justcall(cmd)
        if ret != 0:
            return '0'
        lines = out.split('\n')
        for line in lines:
            if 'total_mem' not in line:
                continue
            l = line.split(':')
            if len(l) < 2:
                continue
            return l[-1]
        return '0'

    def _get_mem_bytes_phy(self):
        cmd = ['free', '-m']
        out, err, ret = justcall(cmd)
        if ret != 0:
            return '0'
        lines = out.split('\n')
        if len(lines) < 2:
            return '0'
        line = lines[1].split()
        if len(line) < 2:
            return '0'
        return line[1]

    def detect_xen(self):
        c = os.path.join(os.sep, 'sys', 'hypervisor', 'uuid')
        self.xenguest = False
        self.xenhv = False
        if not os.path.exists(c):
            return
        with open(c, 'r') as f:
            if '00000000-0000-0000-0000-000000000000' in f.read():
                self.xenhv = True
            else:
                self.xenguest = True

    def is_esx_hv(self):
        return which('vmware-cmd')

    def _get_mem_bytes(self):
        if self.xenhv:
            return self._get_mem_bytes_hv()
        elif self.is_esx_hv():
            s = self._get_mem_bytes_esx()
            if s == '0':
                return self._get_mem_bytes_phy()
        else:
            return self._get_mem_bytes_phy()

    def _get_mem_banks(self):
        if self.container:
            return 'n/a'
        banks =  0
        inBlock = False
        for l in self.dmidecode:
            if not inBlock and l == "Memory Device":
                 inBlock = True
            if inBlock and "Size:" in l:
                 e = l.split()
                 if len(e) == 3:
                     try:
                         size = int(e[1])
                         banks += 1
                     except:
                         pass
        return str(banks)

    def _get_mem_slots(self):
        if self.container:
            return 'n/a'
        for l in self.dmidecode:
            if 'Number Of Devices:' in l:
                return l.split()[-1]
        return '0'

    def _get_os_vendor(self):
        if os.path.exists('/etc/lsb-release'):
            with open('/etc/lsb-release') as f:
                for line in f.readlines():
                    if 'DISTRIB_ID' in line:
                        return line.split('=')[-1].replace('\n','').strip('"')
        if os.path.exists('/etc/debian_version'):
            return 'Debian'
        if os.path.exists('/etc/SuSE-release'):
            return 'SuSE'
        if os.path.exists('/etc/vmware-release'):
            return 'VMware'
        if os.path.exists('/etc/oracle-release'):
            return 'Oracle'
        if os.path.exists('/etc/redhat-release'):
            with open('/etc/redhat-release', 'r') as f:
                buff = f.read()
                if 'CentOS' in buff:
                    return 'CentOS'
                elif 'Oracle' in buff:
                    return 'Oracle'
                else:
                    return 'Red Hat'
        if os.path.exists('/etc/alpine-release'):
            return "Alpine"
        return 'Unknown'

    def _get_os_release_lsb(self):
        if not os.path.exists('/etc/lsb-release'):
            return
        with open('/etc/lsb-release') as f:
            for line in f.readlines():
                if 'DISTRIB_RELEASE' in line:
                    r = line.split('=')[-1].replace('\n','').strip('"')
                    r = r.replace(self._get_os_vendor(), '').strip()
                    if r == "":
                        continue
                    return r
                if 'DISTRIB_DESCRIPTION' in line:
                    r = line.split('=')[-1].replace('\n','').strip('"')
                    r = r.replace(self._get_os_vendor(), '').strip()
                    if r == "":
                        continue
                    return r
        return

    def _get_os_release_debian_version(self):
        if not os.path.exists('/etc/debian_version'):
            return
        with open('/etc/debian_version') as f:
            r = f.read().strip()
        if r == "":
            return
        return r

    def _get_os_release(self):
        files = ['/etc/debian_version',
                 '/etc/vmware-release',
                 '/etc/oracle-release',
                 '/etc/redhat-release']
        if os.path.exists('/etc/SuSE-release'):
            v = []
            with open('/etc/SuSE-release') as f:
                for line in f.readlines():
                    if 'VERSION' in line:
                        v += [line.split('=')[-1].replace('\n','').strip('" ')]
                    if 'PATCHLEVEL' in line:
                        v += [line.split('=')[-1].replace('\n','').strip('" ')]
            return '.'.join(v)
        if os.path.exists('/etc/alpine-release'):
            with open('/etc/alpine-release') as f:
                return f.read().strip()
        r = self._get_os_release_lsb()
        if r:
            return r
        r = self._get_os_release_debian_version()
        if r:
            return r
        if os.path.exists('/etc/oracle-release') and \
           os.path.exists('/etc/redhat-release'):
            with open('/etc/oracle-release') as f1:
                if " VM " in f1.read():
                    with open('/etc/redhat-release') as f2:
                        return f2.read().split('\n')[0].replace(self._get_os_vendor(), '').strip()
        for f in files:
            if os.path.exists(f):
                (out, err, ret) = justcall(['cat', f])
                if ret != 0:
                    return 'Unknown'
                return out.split('\n')[0].replace(self._get_os_vendor(), '').strip()
        return 'Unknown'

    def _get_os_kernel(self):
        (out, err, ret) = justcall(['uname', '-r'])
        if ret != 0:
            return 'Unknown'
        return out.split('\n')[0]

    def _get_os_arch(self):
        if which('arch') is not None:
            cmd = ['arch']
        else:
            cmd = ['uname', '-m']
        out, err, ret = justcall(cmd)
        if ret != 0:
            return 'Unknown'
        return out.split('\n')[0]

    def _get_cpu_freq(self):
        p = '/proc/cpuinfo'
        if not os.path.exists(p):
            return 'Unknown'
        with open(p, 'r') as f:
            for line in f.readlines():
                if 'cpu MHz' in line:
                    return line.split(':')[1].strip().split('.')[0]
        return 'Unknown'

    def _get_cpu_cores(self):
        try:
            with open('/proc/cpuinfo') as f:
                lines = f.readlines()
        except:
            return '0'
        phy = {}
        for line in lines:
            if 'physical id' in line:
                id = line.split(":")[-1].strip()
                if id not in phy:
                    phy[id] = []
            elif 'core id' in line:
                coreid = line.split(":")[-1].strip()
                if coreid not in phy[id]:
                    phy[id].append(coreid)
        n_cores = 0
        for id, coreids in phy.items():
            n_cores += len(coreids)
        if n_cores == 0:
            return self._get_cpu_dies()
        return str(n_cores)

    def _get_cpu_dies_dmi(self):
        if self.container:
            return 'n/a'
        n = 0
        for l in self.dmidecode:
            if 'Processor Information' in l:
                n += 1
        return str(n)

    def _get_cpu_dies_cpuinfo(self):
        try:
            with open('/proc/cpuinfo') as f:
                lines = f.readlines()
        except:
            return '0'
        _lines = set([l for l in lines if 'physical id' in l])
        n_dies = len(_lines)
        if n_dies > 0:
            return str(n_dies)
        # vmware do not show processor physical id
        _lines = [l for l in lines if 'processor' in l]
        n_dies = len(_lines)
        return str(n_dies)

    def _get_cpu_threads(self):
        try:
            with open('/proc/cpuinfo') as f:
                lines = f.readlines()
        except:
            return '0'
        lines = [l for l in lines if 'physical id' in l]
        n_threads = len(lines)
        if n_threads == 0:
            return self._get_cpu_dies()
        return str(len(lines))

    def _get_cpu_dies(self):
        n = self._get_cpu_dies_cpuinfo()
        if n == '0':
            n = self._get_cpu_dies_dmi()
        return n

    def _get_cpu_model(self):
        (out, err, ret) = justcall(['grep', 'model name', '/proc/cpuinfo'])
        if ret != 0:
            return 'Unknown'
        lines = out.split('\n')
        l = lines[0].split(':')
        return l[1].strip()

    def _get_serial_1(self):
        try:
            i = self.dmidecode.index('System Information')
        except ValueError:
            return 'Unknown'
        for l in self.dmidecode[i+1:]:
            if 'Serial Number:' in l:
                return l.split(':')[-1].strip()
        return 'Unknown'

    def _get_serial_2(self):
        """ Dell poweredge 2500 are known to be in this case
        """
        try:
            i = self.dmidecode.index('Chassis Information')
        except ValueError:
            return 'Unknown'
        for l in self.dmidecode[i+1:]:
            if 'Serial Number:' in l:
                return l.split(':')[-1].strip()
        return 'Unknown'

    def _get_serial(self):
        if self.container:
            return 'n/a'
        serial = self._get_serial_1()
        if serial in ('Unknown', 'Not Specified'):
            serial = self._get_serial_2()
        return serial

    def _get_bios_version(self):
        if self.container:
            return 'n/a'
        v = ""
        rev = ""
        try:
            i = self.dmidecode.index('BIOS Information')
        except ValueError:
            return ''
        for l in self.dmidecode[i+1:]:
            if 'Version:' in l:
                v = l.split(':')[-1].strip()
                break
        for l in self.dmidecode[i+1:]:
            if 'BIOS Revision:' in l:
                rev = l.split(':')[-1].strip()
                break
        if len(rev) > 1 and not v.startswith(rev):
            return v+" "+rev
        return v

    def _get_sp_version(self):
        if self.container:
            return 'n/a'
        sp_version = self._get_sp_version_ipmi()
        if sp_version:
            return sp_version
        return ''

    def _get_sp_version_ipmi(self):
        if which("ipmitool") is None:
            return
        cmd = ["ipmitool", "mc", "info"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return
        for l in out.splitlines():
            if 'Firmware Revision' in l:
                v = l.split(' : ')[-1].strip()
                return v
        return v

    def _get_enclosure(self):
        if self.container:
            return 'n/a'
        for l in self.dmidecode:
            if 'Enclosure Name:' in l:
                return l[l.index(":")+1:].strip()
        return 'Unknown'

    def _get_manufacturer(self):
        if self.container:
            return ""
        elif self.xenguest and len(self.dmidecode) < 5:
            return ""
        out, err, ret = justcall(["dmidecode", "-s", "system-manufacturer"])
        if ret != 0:
            return ""
        return out.strip()

    def _get_model(self):
        if self.container:
            return 'container'
        elif self.xenguest and len(self.dmidecode) < 5:
            return "Xen Virtual Machine (PVM)"
        for l in self.dmidecode:
            if 'Product Name:' in l:
                return l[l.index(":")+1:].strip()
        return 'Unknown'

    def get_iscsi_hba_id(self):
        path = os.path.join(os.sep, 'etc', 'iscsi', 'initiatorname.iscsi')
        hba_id = None
        if os.path.exists(path):
            with open(path, 'r') as f:
                hba_id = f.read().split('=')[-1].strip()
        return hba_id

    def _get_hba(self):
        # fc / fcoe
        l = []
        import glob
        paths = glob.glob('/sys/class/fc_host/host*/port_name')
        for path in paths:
            host_link = '/'.join(path.split('/')[0:5])
            if '/eth' in os.path.realpath(host_link):
                hba_type = 'fcoe'
            else:
                hba_type = 'fc'
            with open(path, 'r') as f:
                hba_id = f.read().strip('0x').strip('\n')
            host = path.replace('/sys/class/fc_host/host', '')
            host = host[0:host.index('/')]

            l.append((hba_id, hba_type, host))

        # redhat 4 qla driver does not export hba portname in sysfs
        paths = glob.glob("/proc/scsi/qla2xxx/*")
        for path in paths:
            with open(path, 'r') as f:
                buff = f.read()
                for line in buff.split("\n"):
                    if "adapter-port" not in line:
                        continue
                    _l = line.split("=")
                    if len(_l) != 2:
                        continue
                    host = os.path.basename(path)
                    e = (_l[1].rstrip(";"), "fc", host)
                    if e not in l:
                        l.append(e)

        # iscsi
        path = os.path.join(os.sep, 'etc', 'iscsi', 'initiatorname.iscsi')
        hba_type = 'iscsi'
        hba_id = self.get_iscsi_hba_id()
        if hba_id is not None:
            l.append((hba_id, hba_type, ''))

        # gce
        if self._get_model() == "Google":
            from rcGlobalEnv import rcEnv
            l.append((rcEnv.nodename, "virtual", ''))

        return [{"hba_id": e[0], "hba_type": e[1], "host": e[2]} for e in l]

    def _get_targets(self):
        import glob
        # fc / fcoe
        l = []
        hbas = self._get_hba()
        for hba in hbas:
            if not hba["hba_type"].startswith('fc'):
                continue
            targets = glob.glob('/sys/class/fc_transport/target%s:*/port_name'%hba["host"])
            targets += glob.glob('/sys/class/fc_remote_ports/rport-%s:*/port_name'%hba["host"])
            for target in targets:
                with open(target, 'r') as f:
                    tgt_id = f.read().strip('0x').strip('\n')
                if (hba["hba_id"], tgt_id) not in l:
                    l.append((hba["hba_id"], tgt_id))

        # iscsi
        hba_id = self.get_iscsi_hba_id()
        if hba_id is not None:
            cmd = ['iscsiadm', '-m', 'session']
            out, err, ret = justcall(cmd)
            if ret == 0:
                """
                tcp: [1] 192.168.231.141:3260,1 iqn.2000-08.com.datacore:sds1-1
                tcp: [2] 192.168.231.142:3260,1 iqn.2000-08.com.datacore:sds2-1 (non-flash)
                """
                for line in out.splitlines():
                    if len(line) == 0:
                        continue
                    line = line.replace(" (non-flash)", "")
                    l.append((hba_id, line.split()[-1]))

        # gce
        if self._get_model() == "Google":
            try:
                cmd = ["gcloud", "compute", "regions", "list", "-q", "--format", "json"]
                out, err, ret = justcall(cmd)
                import json
                from rcGlobalEnv import rcEnv
                data = json.loads(out)
                hba_id = rcEnv.nodename
                for region in data:
                    i = region["selfLink"].index("/projects")
                    tgt_id = region["selfLink"][i:].replace("/projects", "").replace("/regions", "")
                    l.append((hba_id, tgt_id))
            except:
                pass

        return [{"hba_id": e[0], "tgt_id": e[1]} for e in l]

    def get_hardware(self):
        devs = []
        devs += self.get_hardware_mem()
        devs += self.get_hardware_pci()
        return devs

    def get_hardware_mem(self):
        out, err, ret = justcall(["dmidecode", "-t", "memory"])
        if ret != 0:
            return []
        devs = []
        dev = None
        path = []
        cla = []
        desc = []
        for line in out.splitlines():
            if line.strip() == "Memory Device":
                # new mem device
                if dev is not None:
                    dev["path"] = " ".join(path)
                    dev["class"] = " ".join(cla)
                    dev["description"] = " ".join(desc)
                    devs.append(dev)
                dev = {
                    "type": "mem",
                    "path": "",
                    "class": "",
                    "description": "",
                    "driver": "",
                }
                path = []
                desc = []
                cla = []
            elif "Locator:" in line:
                path.append(line[line.index(":")+1:].strip())
            elif "Bank Locator:" in line:
                path.append(line[line.index(":")+1:].strip())
            elif "Type:" in line:
                s = line[line.index(":")+1:].strip()
                if s != "Unknown":
                    cla.append(s)
            elif "Type Detail:" in line:
                s = line[line.index(":")+1:].strip()
                if s != "None":
                    cla.append(s)
            elif "  Speed:" in line:
                s = line[line.index(":")+1:].strip()
                if s != "Unknown":
                    cla.append(s)
            elif "Size:" in line:
                s = line[line.index(":")+1:].strip()
                if s != "Unknown":
                    cla.append(s)
            elif "Manufacturer:" in line:
                s = line[line.index(":")+1:].strip()
                if s != "Unknown":
                    desc.append(s)
            elif "Part Number:" in line:
                s = line[line.index(":")+1:].strip()
                if s != "Unknown":
                    desc.append(s)
        if dev is not None:
            dev["path"] = " ".join(path)
            dev["class"] = " ".join(cla)
            dev["description"] = " ".join(desc)
            devs.append(dev)
        return devs
 
    def get_hardware_pci(self):
        out, err, ret = justcall(["lspci", "-v"])
        if ret != 0:
            return []
        devs = []
        dev = None
        for line in out.splitlines():
            if re.match("^\w", line):
                # new pci device
                if dev is not None:
                    devs.append(dev)
                words = line.split()
                path = words.pop(0)
                line = " ".join(words)
                cla = line[:line.index(":")]
                description = line[line.index(":")+1:].strip()
                dev = {
                    "type": "pci",
                    "path": path,
                    "class": cla,
                    "description": description,
                    "driver": "",
                }
            elif "Kernel driver in use:" in line:
                dev["driver"] = line[line.index(":")+1:].strip()
        if dev is not None:
            devs.append(dev)
        return devs
                

if __name__ == "__main__":
    from rcGlobalEnv import rcEnv
    import json
    print(json.dumps(Asset(rcEnv.nodename).get_asset_dict(), indent=4))
0707010005c913000081b40000000000000000000000015ab2175400002aa2000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/rcAssetOSF1.py import os
from rcUtilities import justcall, which
import rcAsset
from distutils.version import LooseVersion as V
import re

sim = False
regex = re.compile("^\W*[0-9]*:")

class Asset(rcAsset.Asset):
    def __init__(self, node):
        rcAsset.Asset.__init__(self, node)

    def convert(self, s, unit):
        if unit == "M":
            return int(float(s))
        elif unit == "G":
            return 1024 * int(float(s))
        else:
            return 0

    def _get_mem_bytes(self):
        # MB
        cmd = ['vmstat', '-P']
        out, err, ret = _justcall(cmd)
        for line in out.split('\n'):
            if 'Total' not in line:
                continue
            l = line.split()
            s = l[-2]
            unit = l[-1]
            mem = self.convert(s, unit)
            return str(mem)
        return '0'

    def _get_mem_banks(self):
        return '0'

    def _get_mem_slots(self):
        return '0'

    def _get_os_vendor(self):
        return 'HP'

    def _get_os_release(self):
        cmd = ['uname', '-a']
        out, err, ret = _justcall(cmd)
        l = out.split()
        return ' '.join(l[2:4])

    def _get_os_kernel(self):
        cmd = ['dupatch', '-track', '-type', 'kit', '-nolog']
        out, err, ret = _justcall(cmd)
        l = []
        for line in out.split('\n'):
            line = line.strip()
            if not line.startswith('- T64') or not 'OSF' in line:
                continue
            l.append(line.split()[1])
        if len(l) == 0:
            return 'Unknown'
        l.sort(lambda x, y: V(x) < V(y))
        return l[-1].split('-')[0]

    def _get_os_arch(self):
        cmd = ['uname', '-a']
        out, err, ret = _justcall(cmd)
        l = out.split()
        return l[-1]

    def _get_cpu_freq(self):
        cmd = ['psrinfo', '-v']
        out, err, ret = _justcall(cmd)
        for line in out.split('\n'):
            if 'operates at' not in line:
                continue
            l = line.split()
            if len(l) < 2:
                continue
            return l[-2]
        return '0'

    def _get_cpu_cores(self):
        return self._get_cpu_dies()

    def _get_cpu_dies(self):
        cmd = ['psrinfo']
        out, err, ret = _justcall(cmd)
        return str(len(out.split('\n'))-1)

    def _get_cpu_model(self):
        cmd = ['psrinfo', '-v']
        out, err, ret = _justcall(cmd)
        for line in out.split('\n'):
            if 'operates at' not in line:
                continue
            l = line.split()
            if len(l) < 3:
                continue
            return l[2]
        return 'Unknown'

    def _get_serial(self):
        cmd = ['consvar', '-g', 'sys_serial_num']
        out, err, ret = _justcall(cmd)
        l = out.split('=')
        if len(l) == 2:
            return l[1].strip()
        return 'Unknown'

    def _get_model(self):
        cmd = ["hwmgr", "-v", "h"]
        out, err, ret = _justcall(cmd)
        for line in out.split('\n'):
            if "platform" not in line:
                continue
            l = line.split("platform")
            if len(l) != 2:
                continue
            return l[1].strip()
        return 'Unknown'

    def is_id(self, line):
        if regex.match(line) is None:
            return False
        return True

    def __get_hba(self):
        # fc / fcoe
        cmd = ['hwmgr', '-show', 'fibre', '-ada']
        out, err, ret = _justcall(cmd)
        hba = {}
        for line in out.split('\n'):
            if self.is_id(line):
                l = line.split()
                hba_name = l[1]
            elif 'WWPN' in line:
                l = line.split()
                hba_portname = l[1].replace('-', '').lower()
                hba[hba_name] = (hba_portname, 'fc')
        return hba

    def _get_hba(self):
        hba = self.__get_hba()
        return hba.values()

    def _get_targets(self):
        # fc / fcoe
        cmd = ['hwmgr', '-show', 'fibre', '-topo']
        out, err, ret = _justcall(cmd)
        tgt = []
        hba = self.__get_hba()
        for line in out.split('\n'):
            if self.is_id(line):
                l = line.split()
                hba_name = l[1]
            elif line.strip().startswith('0x'):
                l = line.split()
                if l[1].startswith('-'):
                    continue
                tgt_portname = l[2].replace('-', '').lower()
                hba_portname = hba[hba_name][0]
                tgt.append((hba_portname, tgt_portname))
        return tgt

def _justcall(cmd):
    if not sim:
        return justcall(cmd)

    data = {}

    data[('hwmgr', '-show', 'fibre', '-ada')] = """

            ADAPTER   LINK    LINK             FABRIC     SCSI     CARD
     HWID:  NAME      STATE   TYPE             STATE      BUS      MODEL
    --------------------------------------------------------------------------------
       53:  emx0      up      point-to-point   attached   scsi3    KGPSA-CA

    		Revisions:  driver 2.17           firmware 3.93A0
    		FC Address: 0x1ece00
    		TARGET:     -1
    		WWPN/WWNN:  1000-0000-c922-585c   2000-0000-c922-585c

            ADAPTER   LINK    LINK             FABRIC     SCSI     CARD
     HWID:  NAME      STATE   TYPE             STATE      BUS      MODEL
    --------------------------------------------------------------------------------
       61:  emx1      up      point-to-point   attached   scsi4    KGPSA-CA

    		Revisions:  driver 2.17           firmware 3.93A0
    		FC Address: 0x1cce00
    		TARGET:     -1
    		WWPN/WWNN:  1000-0000-c924-a43d   2000-0000-c924-a43d

    """

    data[('hwmgr', '-show', 'fibre', '-topo')] = """

            ADAPTER   LINK    LINK             FABRIC     SCSI     CARD
     HWID:  NAME      STATE   TYPE             STATE      BUS      MODEL
    --------------------------------------------------------------------------------
       53:  emx0      up      point-to-point   attached   scsi3    KGPSA-CA

    	FC DID	  TARGET    WWPN     		 WWNN                 lfd  LSIT
    	------------------------------------------------------------------------
    	0x382200      2     5000-1fe1-5012-9d49  5000-1fe1-5012-9d40  l--  L--T
    	0x381200      3     5000-1fe1-5012-9d4f  5000-1fe1-5012-9d40  l--  L--T
    	0x380200      0     5000-1fe1-5012-9d4d  5000-1fe1-5012-9d40  l--  L--T
    	0x383200      1     5000-1fe1-5012-9d4b  5000-1fe1-5012-9d40  l--  L--T
    	0xfffffc     -1     21fc-0005-1e36-2110  1000-0005-1e36-2110  l-d  ----
    	0xfffffe     -1     20ce-0005-1e36-2110  1000-0005-1e36-2110  lf-  ----

            ADAPTER   LINK    LINK             FABRIC     SCSI     CARD
     HWID:  NAME      STATE   TYPE             STATE      BUS      MODEL
    --------------------------------------------------------------------------------
       61:  emx1      up      point-to-point   attached   scsi4    KGPSA-CA

    	FC DID	  TARGET    WWPN     		 WWNN                 lfd  LSIT
    	------------------------------------------------------------------------
    	0xef1200      2     5000-1fe1-5012-9d4e  5000-1fe1-5012-9d40  l--  L--T
    	0xef3200      0     5000-1fe1-5012-9d4a  5000-1fe1-5012-9d40  l--  L--T
    	0xef0200      3     5000-1fe1-5012-9d4c  5000-1fe1-5012-9d40  l--  L--T
    	0xef2200      1     5000-1fe1-5012-9d48  5000-1fe1-5012-9d40  l--  L--T
    	0xfffffc     -1     21fc-0005-1e36-1eee  1000-0005-1e36-1eee  l-d  ----
    	0xfffffe     -1     20ce-0005-1e36-1eee  1000-0005-1e36-1eee  lf-  ----

    """

    data[('hwmgr', '-show', 'scsi', '-full', '-id', '83', '-nowrap')] = """

            SCSI                DEVICE    DEVICE  DRIVER NUM  DEVICE FIRST
     HWID:  DEVICEID HOSTNAME   TYPE      SUBTYPE OWNER  PATH FILE   VALID PATH
    -------------------------------------------------------------------------
       83:  16       wrus01     disk      none    2      8    dsk13  [3/2/1]

          WWID:01000010:6005-08b4-000b-440a-0000-f000-1325-0000


          BUS   TARGET  LUN   PATH STATE
          ---------------------------------
          3     2       1     valid
          3     3       1     valid
          3     1       1     valid
          3     0       1     valid
          4     3       1     valid
          4     2       1     valid
          4     1       1     valid
          4     0       1     valid

    """

    data[('uname', '-a')] = """OSF1 wrus01 V5.1 2650 alpha
    """

    data[('dupatch', '-track', '-type', 'kit', '-nolog')] = """
    Gathering details of relevant patches, this may take a bit of time


    	Patches installed on the system came from following software kits:
    	------------------------------------------------------------------

    	- T64V51BB24AS0003-20030929 OSF540
    	- T64V51BB26AS0005-20050502 IOS540
    	- T64V51BB26AS0005-20050502 OSF540
              ================
                 kernelver

    				NOTE

    	When a patch kit is listed, it does not necessarily mean
    	all patches on that kit are installed on your system.
    """

    data[('psrinfo',)] = """0	on-line   since 05/19/2012 16:00:50
    """

    data[('psrinfo', '-v')] = """Status of processor 0 as of: 06/15/12 18:35:40
      Processor has been on-line since 05/19/2012 16:00:50
      The alpha EV6.8CB (21264C) processor operates at 1000 MHz,
      has a cache size of 8388608 bytes,
      and has an alpha internal floating point processor.
    """

    data[('consvar', '-g', 'sys_serial_num')] = """ sys_serial_num = AY14610125
    """

    data[('hwmgr', '-v', 'h')] = """HWID:   hardware hierarchy
    -------------------------------------------------------------------------------
       1:   platform AlphaServer ES45 Model 2
       2:     cpu CPU0
       6:     bus iop0
       7:       bus hose0
    """

    data[('ifconfig', '-a')] = """ee0: flags=c63<UP,BROADCAST,NOTRAILERS,RUNNING,MULTICAST,SIMPLEX>
         inet 10.6.65.37 netmask fffffc00 broadcast 10.6.67.255 ipmtu 1500
         inet 10.6.66.160 netmask fffffc00 broadcast 10.6.67.255 ipmtu 1500

    ee1: flags=c63<UP,BROADCAST,NOTRAILERS,RUNNING,MULTICAST,SIMPLEX>
         inet 10.40.32.241 netmask fffffc00 broadcast 10.40.35.255 ipmtu 1500

    lo0: flags=100c89<UP,LOOPBACK,NOARP,MULTICAST,SIMPLEX,NOCHECKSUM>
         inet 127.0.0.1 netmask ff000000 ipmtu 4096

    sl0: flags=10<POINTOPOINT>

    tun0: flags=80<NOARP>

    tun1: flags=80<NOARP>
    """

    data[('vmstat', '-P')] = """Total Physical Memory =  1024.00 M
    """

    return data[tuple(cmd)], '', 0


if __name__ == "__main__":
    o = Asset("wrus01")
    print(o._get_mem_bytes())
    print(o._get_os_release())
    print(o._get_os_kernel())
    print(o._get_os_arch())
    print(o._get_cpu_freq())
    print(o._get_cpu_cores())
    print(o._get_cpu_dies())
    print(o._get_cpu_model())
    print(o._get_serial())
    print(o._get_model())
    print(o._get_hba())
    print(o._get_targets())
  0707010005c914000081b40000000000000000000000015ab2175400001f67000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/rcAssetSunOS.py    import os
import datetime
import re
from rcUtilities import justcall, which
from rcGlobalEnv import rcEnv
import rcAsset
from rcZone import is_zone

class Asset(rcAsset.Asset):
    def __init__(self, node=None):
        rcAsset.Asset.__init__(self, node)
        self.osver = 0.
        self.zone = is_zone()

        (out, err, ret) = justcall(['prtdiag'])
        if ret != 0 and len(out) < 4:
            self.prtdiag = []
        else:
            self.prtdiag = out.split('\n')
        (out, err, ret) = justcall(['prtconf'])
        if ret != 0 and len(out) < 4:
            self.prtconf = []
        else:
            self.prtconf = out.split('\n')

    def _get_mem_bytes(self):
        for l in self.prtconf:
            if 'Memory size:' in l:
                return l.split(':')[-1].split()[0]
        return '0'

    def _get_mem_banks(self):
        l = [e for e in self.prtdiag if 'DIMM' in e and 'in use' in e]
        return str(len(l))

    def _get_mem_slots(self):
        l = [e for e in self.prtdiag if 'DIMM' in e]
        return str(len(l))

    def _get_os_vendor(self):
        return 'Oracle'

    def _get_os_name(self):
        f = '/etc/release'
        if os.path.exists(f):
            (out, err, ret) = justcall(['cat', f])
            if ret != 0:
                return 'Unknown'
            if 'OpenSolaris' in out:
                return 'OpenSolaris'
        return 'SunOS'

    def _get_os_release(self):
        f = '/etc/release'
        if os.path.exists(f):
            (out, err, ret) = justcall(['cat', f])
            if ret != 0:
                return 'Unknown'
            return out.split('\n')[0].replace('OpenSolaris','').replace('Oracle', '').strip()
        return 'Unknown'

    def _get_os_kernel(self):
        (out, err, ret) = justcall(['uname', '-v'])
        if ret != 0:
            return 'Unknown'
        lines = out.split('\n')
        if len(lines) == 0:
            return 'Unknown'
        try:
            self.osver = float(lines[0])
        except:
            return lines[0]
        if self.osver < 11.:
            return lines[0]
        else:
            (out, err, ret) = justcall(['pkg', 'info', 'entire'])
            if ret != 0:
                return 'Unknown'
            nfo = out.split('\n')
            for l in nfo:
                if 'Version: ' in l:
                    if 'SRU' in l:
                        return ' '.join([lines[0], 'SRU', l.split()[6].strip(')')])
                    elif lines[0] in l:
                        return l.split()[4].strip(')')
                    else:
                        return ' '.join([lines[0], l.split()[4]])
        return 'Unknown'

    def _get_os_arch(self):
        (out, err, ret) = justcall(['uname', '-m'])
        if ret != 0:
            return 'Unknown'
        return out.split('\n')[0]

    def _get_cpu_freq(self):
        (out, err, ret) = justcall(['/usr/sbin/psrinfo', '-pv'])
        if ret != 0:
            return '0'
        for w in out.split():
            if 'MHz)' in w:
                return prev
            prev = w
        (out, err, ret) = justcall(['kstat', 'cpu_info'])
        if ret != 0:
            return '0'
        l = out.split()
        if 'clock_MHz' in l:
            freq = l[l.index('clock_MHz')+1]
            return freq
        return '0'

    def _get_cpu_cores(self):
        cmd = ['kstat', 'cpu_info']
        out, err, ret = justcall(cmd)
        if ret != 0:
            return '0'
        core_ids = set()
        if "core_id" in out:
            keyword = "core_id"
        else:
            keyword = "chip_id"
        for line in out.split('\n'):
            if not line.strip().startswith(keyword):
                continue
            core_ids.add(line.split()[-1])
        return str(len(core_ids))

    def _get_cpu_threads(self):
        out, err, ret = justcall(['/usr/sbin/psrinfo'])
        if ret != 0:
            return '0'
        return str(len(out.split('\n'))-1)

    def _get_cpu_dies(self):
        (out, err, ret) = justcall(['/usr/sbin/psrinfo', '-p'])
        if ret != 0:
            return '0'
        return out.split('\n')[0]

    def _get_cpu_model(self):
        (out, err, ret) = justcall(['/usr/sbin/psrinfo', '-pv'])
        if ret != 0:
            return 'Unknown'
        lines = out.split('\n')
        lines = [line for line in lines if len(line) > 0]
        if len(lines) == 0:
            return 'Unknown'
        model = lines[-1].strip()
        if model.startswith('The '):
            model = model.replace('The ', '')
        known_garbage = [' (chipid', ' (portid', ' physical proc']
        for s in known_garbage:
            try:
                i = model.index(s)
                model = model[:i]
            except ValueError:
                continue
        return model

    def _get_serial(self):
        if which("sneep"):
            cmd = ['sneep']
        else:
            cmd = ['hostid']
        (out, err, ret) = justcall(cmd)
        if ret != 0:
            return 'Unknown'
        return out.split('\n')[0]

    def _get_model(self):
        if self.zone:
            return "Solaris Zone"
        for l in self.prtdiag:
            if 'System Configuration:' in l:
                return l.split(':')[-1].strip()
        return 'Unknown'

    def __get_hba(self):
        # fc / fcoe
        """
        # cfgadm -s match="exact,select=type(fc-fabric)"
        Ap_Id      Type         Receptacle   Occupant     Condition
        c5         fc-fabric    connected    configured   unknown
        """
        l = []
        if not which('cfgadm'):
            return []
        if not which('luxadm'):
            return []
        cmd = ['cfgadm', '-lv', '-s', 'match=exact,select=type(fc-fabric)']
        out, err, ret = justcall(cmd)
        if ret != 0:
            return []
        words = out.split()
        hba_names = [word for word in words if word.startswith("/devices/")]

        if len(hba_names) == 0:
            return []

        hba_type = 'fc'
        for hba_name in hba_names:
            targets = []
            cmd = ['luxadm', '-e', 'dump_map', hba_name]
            out, err, ret = justcall(cmd)
            if ret != 0:
                continue
            lines = out.split('\n')
            if len(lines) < 2:
                continue
            for line in lines[1:]:
                words = line.split()
                if len(words) < 5:
                    continue
                if 'Host Bus' in line:
                    hba_id = words[3]
                else:
                    targets.append(words[3])
            l.append((hba_id, hba_type, targets))

        return l

    def _get_hba(self):
        l = self.__get_hba()
        return [{"hba_id": e[0], "hba_type": e[1]} for e in l]

    def _get_targets(self):
        l = self.__get_hba()
        m = []
        for hba_id, hba_type, targets in l:
             for target in targets:
                 m.append({"hba_id": hba_id, "tgt_id": target})
        return m

    def _get_bios_version(self):
        arch = self._get_os_arch().lower()
        if arch.startswith("sun4"):
            return self._get_bios_version_sparc()
        else:
            return self._get_bios_version_intel()

    def _get_bios_version_sparc(self):
        for l in self.prtdiag:
            if l.startswith("OBP "):
                v = l.replace("OBP ", "").strip()
                v = re.sub(' [0-9]{4}/[0-9]{2}/[0-9]{2} [0-9]{2}:[0-9]{2}', '', v)
                return v
        return ''

    def _get_bios_version_intel(self):
        if which('smbios') is None:
            return ''
        out, err, ret = justcall(['smbios'])
        if ret != 0:
            return ''
        try:
            i = out.index('BIOS information')
        except ValueError:
            return ''
        for l in out[i:].splitlines():
            if 'Version String' in l:
                return l.split(':')[-1].strip()
        return ''


if __name__ == "__main__":
    print(Asset()._get_cpu_model())
 0707010005c915000081b40000000000000000000000015ab2175400002280000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/rcAssetWindows.py  import os
import sys
import platform
import datetime
from rcUtilities import justcall, which, try_decode
from rcUtilitiesWindows import get_registry_value
import rcAsset
import ctypes
import wmi
from rcDiskInfoWindows import diskInfo
from converters import convert_size

class MEMORYSTATUSEX(ctypes.Structure):
    _fields_ = [("dwLength", ctypes.c_uint),
                ("dwMemoryLoad", ctypes.c_uint),
                ("ullTotalPhys", ctypes.c_ulonglong),
                ("ullAvailPhys", ctypes.c_ulonglong),
                ("ullTotalPageFile", ctypes.c_ulonglong),
                ("ullAvailPageFile", ctypes.c_ulonglong),
                ("ullTotalVirtual", ctypes.c_ulonglong),
                ("ullAvailVirtual", ctypes.c_ulonglong),
                ("sullAvailExtendedVirtual", ctypes.c_ulonglong),]

    def __init__(self):
        # have to initialize this to the size of MEMORYSTATUSEX
        self.dwLength = 2*4 + 7*8     # size = 2 ints, 7 longs
        return super(MEMORYSTATUSEX, self).__init__()


class Asset(rcAsset.Asset):
    def __init__(self, node):
        self.w = wmi.WMI()
        self.cpuinfo = self.w.Win32_Processor()
        rcAsset.Asset.__init__(self, node)
        self.memstat = MEMORYSTATUSEX()
        ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(self.memstat))

    def _get_tz(self):
        # TODO: return in fmt "+01:00"
        return

    def _get_mem_bytes(self):
        return str(self.memstat.ullTotalPhys // 1024 // 1024)

    def _get_mem_banks(self):
        md = len(self.w.WIN32_PhysicalMemory())
        return str(md)

    def _get_mem_slots(self):
        n = 0
        for a in self.w.WIN32_PhysicalMemoryArray():
            n += a.MemoryDevices
        return str(n)

    def _get_os_vendor(self):
        return 'Microsoft'

    def _get_os_name(self):
        return 'Windows'

    def _get_os_release(self):
        v = sys.getwindowsversion()
        product = {
         1: 'Workstation',
         2: 'Domain Controller',
         3: 'Server',
        }
        s = platform.release()
        s = s.replace('Server', ' Server')
        s = s.replace('ServerR', ' Server R')
        s = s.replace('Workstation', ' Workstation')
        s += " %s" % v.service_pack
        return s

    def _get_os_kernel(self):
        v = sys.getwindowsversion()
        return ".".join(map(str, [v.major, v.minor, v.build]))

    def _get_os_arch(self):
        return platform.uname()[4]

    def _get_cpu_freq(self):
        for i in self.cpuinfo:
            cpuspeed = i.MaxClockSpeed
        return str(cpuspeed)

    def _get_cpu_cores(self):
        n = 0
        for p in self.cpuinfo:
            try:
                cores = p.NumberOfCores
            except:
                cores = 1
            n += cores
        return str(n)

    def _get_cpu_dies(self):
        s = set()
        for p in self.cpuinfo:
            s.add(p.SocketDesignation)
        n = len(s)
        return str(n)

    def _get_cpu_model(self):
        for i in self.cpuinfo:
            cputype = i.Name
        return cputype

    def _get_enclosure(self):
        for i in self.w.Win32_SystemEnclosure():
            name = i.Name
        return name

    def _get_serial(self):
        for i in self.w.Win32_ComputerSystemProduct():
            name = i.IdentifyingNumber
        return name

    def _get_model(self):
        for i in self.w.Win32_ComputerSystemProduct():
            name = i.Name
        return name

    def _get_hba(self):
        hbas = []
        self.di = diskInfo()
        for index, portwwn, host in self.di._get_fc_hbas():
            hbas.append((portwwn, 'fc'))
        return hbas

    def _get_targets(self):
        maps = []
        if not which('fcinfo'):
            print('  fcinfo is not installed')
            return []
        for index, portwwn, host in self.di._get_fc_hbas():
            cmd = ['fcinfo', '/mapping', '/ai:'+index]
            out, err, ret = justcall(cmd)
            if ret != 0:
                print('error executing', ' '.join(cmd), out, err, ret)
                continue
            for line in out.split('\n'):
                if not line.startswith('(x'):
                    continue
                l = line.split()
                if len(l) < 3:
                    continue
                tgtportwwn = l[2].strip(',').replace(':', '')
                if (portwwn, tgtportwwn) in maps:
                    continue
                maps.append((portwwn, tgtportwwn))
        return maps

    def get_last_boot(self):
        payload = self.w.Win32_PerfFormattedData_PerfOS_System()
        uptime = payload[-1].SystemUpTime
        try:
            last = datetime.datetime.now() - datetime.timedelta(seconds=int(uptime))
        except:
            return
        last = last.strftime("%Y-%m-%d")
        return {
            "title": "last_boot",
            "value": last,
            "source": "probe",
        }

    def get_hardware(self):
        devs = []
        devs += self.get_hardware_devs()
        devs += self.get_hardware_mem()
        return devs

    def get_hardware_mem(self):
        '''
        Get-WmiObject -Class "win32_PhysicalMemory"
		instance of Win32_PhysicalMemory
        {
        Attributes = 0;
        BankLabel = "";
        Capacity = "2147483648";
        Caption = "Memoire physique";
        ConfiguredClockSpeed = 0;
        ConfiguredVoltage = 0;
        CreationClassName = "Win32_PhysicalMemory";
        Description = "Memoire physique";
        DeviceLocator = "DIMM 0";
        FormFactor = 8;
        Manufacturer = "QEMU";
        MaxVoltage = 0;
        MemoryType = 9;
        MinVoltage = 0;
        Name = "Memoire physique";
        SMBIOSMemoryType = 7;
        Tag = "Physical Memory 0";
        TypeDetail = 2;
        };
		'''
        devs = []
        dev = None
        path = []
        cla = []
        desc = []
        payload = self.w.WIN32_PhysicalMemory()
        for a in payload:
            path = []
            cla = []
            desc = []
            dev = {
                    "type": "mem",
                    "path": "",
                    "class": "",
                    "description": "",
                    "driver": "",
                }
            path.append(a.DeviceLocator)
            if len(a.BankLabel) > 0:
                path.append(a.BankLabel)
            desc.append(a.Description)
            desc.append(a.Manufacturer)
            size = str(convert_size(a.Capacity, _to="GB"))+'GB'
            cla.append(size)
            if dev is not None:
                dev["path"] = " ".join(path)
                dev["class"] = " ".join(cla)
                dev["description"] = " ".join(desc)
                devs.append(dev)
        return devs

    def get_hardware_devs(self):
        '''
        Get-WmiObject -Class "Win32_PnpSignedDriver"
		'''
        devs = []
        dev = None
        path = []
        cla = []
        desc = []
        payload = self.w.Win32_PnpSignedDriver()
        for a in payload:
            path = []
            cla = []
            desc = []
            type = []
            driver = []
            dev = {
                    "type": "",
                    "path": "",
                    "class": "",
                    "description": "",
                    "driver": "",
                }
            if a.Description is not None:
                desc.append(a.Description)
            if a.Manufacturer is not None:
                desc.append(a.Manufacturer)
            if len(desc) == 0:
                desc.append(a.DeviceID)
            if a.DeviceClass is not None:
                cla.append(a.DeviceClass)
            if a.Location is not None:
                if 'PCI bus' in str(a.Location):
                    type.append('pci')
                    pciinfo = a.Location.split(',')
                    pcibus = pciinfo[0].split(' ')[-1]
                    device = pciinfo[1].split(' ')[-1]
                    function = pciinfo[2].split(' ')[-1]
                    string = str("%02d"%int(pcibus) + ':' + "%02d"%int(device) + '.' + function)
                    path.append(string)
                else:
                    path.append(a.Location)
            if a.DriverProviderName is not None:
                driver.append(a.DriverProviderName)
            if a.InfName is not None:
                driver.append(a.InfName)
            if a.DriverVersion is not None:
                driver.append(a.DriverVersion)    
            if dev is not None:
                dev["path"] = " ".join(path)
                dev["type"] = " ".join(type)
                dev["class"] = " ".join(cla)
                dev["driver"] = " ".join(driver)
                dev["description"] = " ".join(desc)
                devs.append(dev)
        return devs0707010005c916000081b40000000000000000000000015ab21754000010a7000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/rcBrocade.py   from rcUtilities import justcall, which

import os
import telnetlib

try:
    import ConfigParser
except ImportError:
    import configparser as ConfigParser

import rcExceptions as ex
from rcGlobalEnv import rcEnv

if rcEnv.paths.pathbin not in os.environ['PATH']:
    os.environ['PATH'] += ":"+rcEnv.paths.pathbin

def brocadetelnetcmd(cmd, switch, username, password):
    tn = telnetlib.Telnet(switch)
    tn.read_until("login: ")
    tn.write(username + '\n')
    tn.read_until("Password: ")
    tn.write(password + '\n')
    tn.read_until("> ")
    tn.write(cmd + '\n')
    tn.write('exit\n')
    out = tn.read_all()
    return out, "", 0

def brocadecmd(cmd, switch, username, key):
    _cmd = ['ssh', '-o', 'StrictHostKeyChecking=no',
                   '-o', 'ForwardX11=no',
                   '-o', 'ConnectTimeout=5',
                   '-o', 'PasswordAuthentication=no',
                   '-l', username, '-i', key, switch, cmd]
    out, err, ret = justcall(_cmd)
    if "command not found" in err:
        # bogus firmware syntax
        _cmd = ['ssh', '-o', 'StrictHostKeyChecking=no',
                       '-o', 'ForwardX11=no',
                       '-o', 'ConnectTimeout=5',
                       '-o', 'PasswordAuthentication=no',
                       '-l', username, '-i', key, switch, 'bash --login -c '+cmd]
        out, err, ret = justcall(_cmd)
    if ret != 0:
        raise ex.excError("brocade command execution error")
    return out, err, ret

class Brocades(object):
    switchs = []

    def __init__(self, objects=[]):
        self.objects = objects
        if len(objects) > 0:
            self.filtering = True
        else:
            self.filtering = False
        cf = rcEnv.paths.authconf
        if not os.path.exists(cf):
            return
        conf = ConfigParser.RawConfigParser()
        conf.read(cf)
        m = []
        for s in conf.sections():
            if self.filtering and s not in self.objects:
                continue
            try:
                stype = conf.get(s, 'type')
            except:
                continue
            if stype != "brocade":
                continue
            name = s
            key = None
            password = None
            try:
                username = conf.get(s, 'username')
            except:
                print("no 'username' parameter in %s section %s"%(cf, s))
                continue
            try:
                key = conf.get(s, 'key')
            except:
                pass
            try:
                password = conf.get(s, 'password')
            except:
                pass
            if key is None and password is None:
                print("no 'key' nor 'password' parameter in %s section %s"%(cf, s))
                continue
            m.append([name, username, key, password])
        del(conf)
        for name, username, key, password in m:
            self.switchs.append(Brocade(name, username, key, password))

    def __iter__(self):
        for switch in self.switchs:
            yield(switch)

class Brocade(object):
    def __init__(self, name, username, key, password):
        self.name = name
        self.username = username
        self.password = password
        self.key = key
        self.keys = ['brocadeswitchshow', 'brocadensshow', 'brocadezoneshow']

    def brocadecmd(self, cmd):
        if self.key is not None:
            return brocadecmd(cmd, self.name, self.username, self.key)
        elif self.password is not None:
            return brocadetelnetcmd(cmd, self.name, self.username, self.password)
        else:
            raise Exception("ssh nor telnet method available")

    def get_brocadeswitchshow(self):
        cmd = 'switchshow'
        print("%s: %s"%(self.name, cmd))
        buff = self.brocadecmd(cmd)[0]
        return buff

    def get_brocadensshow(self):
        cmd = 'nsshow'
        print("%s: %s"%(self.name, cmd))
        buff = self.brocadecmd(cmd)[0]
        return buff

    def get_brocadezoneshow(self):
        cmd = 'zoneshow'
        print("%s: %s"%(self.name, cmd))
        buff = self.brocadecmd(cmd)[0]
        return buff

if __name__ == "__main__":
    o = Brocades()
    for brocade in o:
        print(brocade.get_brocadeswitchshow())
 0707010005c917000081b40000000000000000000000015ab21754000034e5000000660000000000000000000000000000002600000000root/usr/share/opensvc/lib/rcBtrfs.py from rcUtilities import justcall, vcall
from rcUtilitiesLinux import label_to_dev
import sys
import os
import logging
from rcGlobalEnv import rcEnv

class InitError(Exception):
     pass

class ExecError(Exception):
     pass

class ExistError(Exception):
     pass

def btrfs_devs(mnt):
    out, err, ret = justcall(["btrfs", "fi", "show", mnt])
    if ret != 0:
        return []
    devs = []
    for line in out.splitlines():
        line = line.strip()
        if not line.startswith("devid"):
            continue
        dev = line.split(" path ")[-1]
        devs.append(dev)
    return devs

class Btrfs(object):
    log = None
    #snapvol = ".osvcsnap"
    snapvol = ""

    def __init__(self, path=None, label=None, node=None, resource=None):
        self.path = path
        self.label = label
        self.node = node
        self.resource = resource

        if self.resource is None:
            if Btrfs.log is None:
                Btrfs.log = logging.getLogger("BTRFS")
                Btrfs.log.addHandler(logging.StreamHandler(sys.stdout))
                Btrfs.log.setLevel(logging.INFO)
            self.log = Btrfs.log
        else:
            self.log = self.resource.log

        if path is not None:
            if not self.dir_exists(path):
                raise InitError("path %s does not exist"%path)
            self.get_label_from_path(path)

        if self.label is None:
            raise InitError("failed to determine btrfs label")

        self.setup_rootvol()
        self.path = self.rootdir
        self.snapdir = os.path.join(self.rootdir, self.snapvol)
        self.snapdir = os.path.normpath(self.snapdir)


    def get_dev(self):
        if hasattr(self, "dev"):
            return
        if self.node is None:
            try:
                self.dev = label_to_dev("LABEL="+self.label,
                                        tree=self.resource.svc.node.devtree)
            except ex.excError as exc:
                self.dev = None
        else:
            return
        if self.dev is None:
            self.dev = "LABEL="+self.label

    def rmdir(self, path):
        cmd = ['rmdir', path]
        out, err, ret = self.justcall(cmd)
        if ret != 0:
            raise ExecError("error removing dir %s:\n%s"%(path,err))

    def dir_exists(self, path):
        cmd = ['test', '-d', path]
        out, err, ret = self.justcall(cmd)
        if ret > 1:
            raise ExecError("error joining remote node %s:\n%s"%(self.node, err))
        if ret == 1:
            return False
        return True

    def get_subvols(self, refresh=False):
        """
        ID 256 parent 5 top level 5 path btrfssvc
        ID 259 parent 256 top level 5 path btrfssvc/child
        ID 260 parent 5 top level 5 path btrfssvc@sent
        ID 261 parent 256 top level 5 path btrfssvc/child@sent
        ID 262 parent 5 top level 5 path btrfssvc@tosend
        ID 263 parent 256 top level 5 path btrfssvc/child@tosend
        ID 264 parent 5 top level 5 path subdir/vol
        ID 265 parent 256 top level 5 path btrfssvc/cross_mnt_snap
        """
        if not refresh and hasattr(self, "subvols"):
            return
        self.subvols = {}
        cmd = ['btrfs', 'subvol', 'list', '-p', self.path]
        out, err, ret = self.justcall(cmd)
        if ret != 0:
            raise InitError("error running btrfs subvol list %s:\n"%self.path+err)

        for line in out.split("\n"):
            if len(line) == 0:
                continue
            l = line.split()
            subvol = {}
            subvol['id'] = l[1]
            subvol['parent_id'] = l[3]
            subvol['top'] = l[6]
            subvol['path'] = line[line.index(" path ")+6:]
            self.subvols[subvol['id']] = subvol

    def subvol_delete(self, subvol=[], recursive=False):
        opts = []
        if recursive:
            opts.appendi('-R')

        if isinstance(subvol, list):
            subvols = subvol
        else:
            subvols = [subvol]

        # delete in descending depth order
        subvols.sort(reverse=True)

        cmd = []
        for subvol in subvols:
            if not self.has_subvol(subvol):
                continue
            cmd += ['btrfs', 'subvolume', 'delete'] + opts + [subvol, '&&']

        if len(cmd) == 0:
            return

        cmd = cmd[:-1]

        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ExecError()

    def get_subvols_in_path(self, path):
        self.get_subvols(refresh=True)
        head = self.path_to_subvol(path)
        subvols = [path]
        for subvol in self.subvols.values():
            if subvol['path'].startswith(head+'/'):
                subvols.append(self.rootdir+'/'+subvol['path'])
        return subvols

    def snapshot(self, origin, snap, readonly=False, recursive=False):
        if self.has_subvol(snap):
            raise ExistError("snapshot %s already exists"%snap)

        opts = []
        if recursive:
            opts.append('-R')
        if readonly:
            opts.append('-r')

        cmd = ['btrfs', 'subvolume', 'snapshot'] + opts + [origin, snap]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ExecError(err)

    def has_snapvol(self):
        return self.has_subvol(self.snapvol)

    def path_to_subvol(self, path):
        if path.startswith('/'):
            return path.replace(self.rootdir+'/', "")
        return path

    def has_subvol(self, subvol):
        # refresh subvol list
        self.get_subvols(refresh=True)

        subvol = self.path_to_subvol(subvol)
        for sub in self.subvols.values():
            if sub['path'] == subvol:
                return True
        return False

    def mount_snapvol(self):
        self.get_dev()
        cmd = ['mount', '-t', 'btrfs', '-o', 'subvol='+self.snapvol, self.dev, self.snapdir]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ExecError("error mounting %s subvol:\ncmd: %s\n%s"%(self.label,' '.join(cmd),err))

    def mount_rootvol(self):
        if self.node:
            return
        self.get_dev()
        if self.is_mounted_subvol(self.rootdir):
            return
        cmd = ['mount', '-t', 'btrfs', '-o', 'subvolid=0', self.dev, self.rootdir]
        out, err, ret = self.justcall(cmd)
        if ret != 0:
            raise ExecError("error mounting %s btrfs:\ncmd: %s\n%s"%(self.label,' '.join(cmd),err))

    def create_snapvol(self):
        self.get_dev()
        error = False

        import tempfile
        tmpdir = tempfile.mktemp()
        cmd = ['mkdir', '-p', tmpdir]
        out, err, ret = self.justcall(cmd)
        if ret != 0:
            raise ExecError("error creating dir %s:\n"%tmpdir+err)

        cmd = ['mount', '-t', 'btrfs', '-o', 'subvolid=0', self.dev, tmpdir]
        out, err, ret = self.justcall(cmd)
        if ret != 0:
            self.rmdir(tmpdir)
            raise ExecError("error mounting %s btrfs:\ncmd: %s\n%s"%(self.label,' '.join(cmd),err))

        try:
            self.create_subvol(os.path.join(tmpdir, self.snapvol))
        except:
            error = True

        cmd = ['umount', tmpdir]
        out, err, ret = self.justcall(cmd)
        if ret != 0:
            raise ExecError("error umounting %s btrfs:\n"%self.label+err)
        self.rmdir(tmpdir)
        if error:
            raise ExecError("failed to create %s"%self.snapvol)

    def vcall(self, cmd):
        if self.node is not None:
            cmd = [' '.join(cmd)]
            cmd = rcEnv.rsh.split() + [self.node] + cmd

        return vcall(cmd, log=self.log)

    def justcall(self, cmd):
        if self.node is not None:
            cmd = [' '.join(cmd)]
            cmd = rcEnv.rsh.split() + [self.node] + cmd
        return justcall(cmd)

    def create_subvol(self, path):
        cmd = ['btrfs', 'subvol', 'create', path]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ExecError("error creating %s subvol"%path)

    def setup_snap_subvol(self):
        # unused for now


        if not self.dir_exists(self.snapdir):
            cmd = ['mkdir', '-p', self.snapdir]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                raise ExecError("error creating dir %s:\n"%self.snapdir+err)

        if not self.has_snapvol():
            self.create_snapvol()
            self.mount_snapvol()

        try:
            o = Btrfs(self.snapdir)
        except InitError:
            self.mount_snapvol()
            o = Btrfs(self.snapdir)

        if o.label != self.label:
            raise ExecError("wrong fs mounted in %s: %s"%(self.snapdir, o.label))

        # verify this is the right subvol (missing: path->subvol name fn)

    def setup_rootvol(self):
        self.rootdir = os.path.join(rcEnv.paths.pathvar, 'btrfs', self.label)

        if not self.dir_exists(self.rootdir):
            cmd = ['mkdir', '-p', self.rootdir]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                raise ExecError("error creating dir %s:\n"%self.rootdir+err)

        self.mount_rootvol()

    def setup_snap(self):
        if not self.has_snapvol():
            self.create_subvol(self.snapdir)
        try:
            o = Btrfs(self.snapdir)
        except InitError:
            self.mount_snapvol()
            o = Btrfs(self.snapdir)

        if o.label != self.label:
            raise ExecError("wrong fs mounted in %s: %s"%(self.snapdir, o.label))

        # verify this is the right subvol (missing: path->subvol name fn)

    def get_mounts(self):
        """
        /dev/vdb on /data type btrfs (rw) [data]
        """
        cmd = ['mount', '-t', 'btrfs', '-l']
        out, err, ret = self.justcall(cmd)
        if ret != 0:
            raise InitError("error running %s:\n"%' '.join(cmd)+err)
        mounts = {}
        for line in out.split("\n"):
            if len(line) == 0 or " on " not in line or " type btrfs " not in line:
                continue
            mntpt = line[line.index(" on ")+4:line.index(" type btrfs ")]
            if '[' in line:
                l = line.split('[')
                label = l[-1].strip(']')
            else:
                label = self.get_label(mntpt)
            mounts[mntpt] = label
        return mounts

    def get_label(self, mntpt):
        cmd = ['btrfs', 'fi', 'label', mntpt]
        out, err, ret = self.justcall(cmd)
        if ret != 0:
            raise excError("error running %s:\n"%' '.join(cmd)+err)
        return out.strip('\n')

    def is_mounted_subvol(self, path):
        path = path.rstrip('/')
        for mntpt, label in self.get_mounts().items():
            if mntpt == path and label == self.label:
                return True
        return False

    def get_label_from_path(self, path):
        path = path.rstrip('/')

        mounts = self.get_mounts()
        l = path.split('/')
        while len(l) > 0:
            m = '/'.join(l)
            if m in mounts:
                self.label = mounts[m]
                return
            l = l[:-1]

        raise InitError("could not get label from path %s"%path)

    def parse_fi_show(self):
        """
        Label: 'data'  uuid: 0d05d0b9-ffab-4ab8-b923-15a38ec806d5
                Total devices 2 FS bytes used 48.92MB
                devid    2 size 5.00GB used 1.51GB path /dev/vdc
                devid    1 size 5.00GB used 1.53GB path /dev/vdb
        """
        cmd = ['btrfs', 'fi', 'show', path]
        out, err, ret = self.justcall(cmd)
        if ret != 0:
            raise InitError("error running btrfs fi show:\n"+err)

        self.devs = {}
        for line in out.split("\n"):
            if "Label:" in line:
                l = line.split("'")
                if len(l) != 2:
                    raise InitError("unexpected line format: "+line)
                label = l[1]

                l = line.split()
                uuid = l[-1]
            elif line.strip().startswith("devid"):
                l = line.split()
                self.devs[l[-1]] = (label, uuid)

    def get_transid(self, path):
        """
        /opt/opensvc/var/btrfs/win2/win2@sent
                Name:                   win2@sent
                uuid:                   167af15f-7d5a-2745-966c-dde4aaa056b7
                Parent uuid:            30121b33-a10f-a642-8caf-0184f5d8e5b0
                Creation time:          2015-09-02 04:01:20
                Object ID:              549
                Generation (Gen):       591564
                Gen at creation:        591564
                Parent:                 5
                Top Level:              5
                Flags:                  readonly
                Snapshot(s):
        """
        cmd = ['btrfs', 'subvolume', 'show', path]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ExecError("can't fetch %s transid:\n%s"%(path, err))
        for line in out.split("\n"):
            if "Generation" in line:
                return line.split()[-1]
        raise ExecError("can't find %s transid\n"%path)

    def __str__(self):
        self.get_subvols()
        s = "label: %s\n" % self.label
        s += "subvolumes:\n"
        for sub in self.subvols.values():
            s += "id: %s parent_id: %s top: %s path: %s\n"%(sub['id'], sub['parent_id'], sub['top'], sub['path'])
        return s

if __name__ == "__main__":
    o = Btrfs(label=sys.argv[1])
    print(o.get_transid("/opt/opensvc/var/btrfs/deb1/deb1@sent"))
    #o.setup_snap()

   0707010005c918000081b40000000000000000000000015ab2175400000c06000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/rcCentera.py   from __future__ import print_function

import os
import tempfile
from subprocess import *

try:
    import ConfigParser
except ImportError:
    import configparser as ConfigParser

import rcExceptions as ex
from rcUtilities import which
from rcGlobalEnv import rcEnv

if rcEnv.paths.pathbin not in os.environ['PATH']:
    os.environ['PATH'] += ":"+rcEnv.paths.pathbin

class Centeras(object):
    def __init__(self, objects=[]):
        self.objects = objects
        if len(objects) > 0:
            self.filtering = True
        else:
            self.filtering = False
        self.arrays = []
        cf = rcEnv.paths.authconf
        if not os.path.exists(cf):
            return
        conf = ConfigParser.RawConfigParser()
        conf.read(cf)
        m = {}

        for s in conf.sections():
            if not conf.has_option(s, "type") or \
               conf.get(s, "type") != "centera":
                continue

            if self.filtering and not s in self.objects:
                continue

            server = None
            username = None
            password = None

            kwargs = {}

            for key in ("server", "username", "password", "java_bin", "jcass_dir"):
                try:
                    kwargs[key] = conf.get(s, key)
                except:
                    print("missing parameter: %s", s)
                    continue

            self.arrays.append(Centera(s, **kwargs))

        del(conf)

    def __iter__(self):
        for array in self.arrays:
            yield(array)

class Centera(object):
    def __init__(self, name, server=None, username=None, password=None, java_bin=None, jcass_dir=None):
        self.name = name
        self.server = server
        self.username = username
        self.password = password
        self.java_bin = java_bin
        self.jcass_dir = jcass_dir
        self.keys = ['discover']

    def rcmd(self, buff):
        current_ld = os.environ.get("LD_LIBRARY_PATH", "")
        if self.jcass_dir not in os.environ.get("LD_LIBRARY_PATH", ""):
            os.environ["LD_LIBRARY_PATH"] = current_ld+":"+self.jcass_dir
        cmd = [self.java_bin, "-jar", os.path.join(self.jcass_dir, "JCASScript.jar")]
        buff = "poolopen %s?name=%s,secret=%s\n" % (self.server, self.username, self.password) + buff + "\nquit"
        p = Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE)
        out, err = p.communicate(input=buff)
        out = out.replace(self.password, "*****")
        err = err.replace(self.password, "*****")
        print(out, err)
        return out, err

    def get_discover(self):
        f = tempfile.NamedTemporaryFile(prefix="centera.discover.", suffix=".xml", dir=rcEnv.paths.pathtmp)
        tmp_fpath = f.name
        f.close()
        buff = "monitorDiscoverToFile %s" % tmp_fpath
        out, err = self.rcmd(buff)
        with open(tmp_fpath, "r") as f:
            s = f.read()
        os.unlink(tmp_fpath)
        return s

if __name__ == "__main__":
    o = Centeras()
    for centera in o:
        print(centera.get_discover())
        break

  0707010005c919000081b40000000000000000000000015ab21754000001bc000000660000000000000000000000000000002600000000root/usr/share/opensvc/lib/rcCloud.py import socket
import rcExceptions as ex

class Cloud(object):
    def __init__(self, s, auth):
        self.cid = s
        self.auth = auth

    def list_svcnames(self):
         print("todo")
         return []

    def list_nodes(self):
        try:
            nodes = self.driver.list_nodes()
        except socket.error as e:
            raise ex.excError("error connecting to %s cloud url (%s)"%(self.cid, str(e)))
        return nodes

0707010005c91a000081b40000000000000000000000015ab2175400000503000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/rcCloudAmazon.py   import rcCloud
import rcExceptions as ex
import socket

try:
    from libcloud.compute.types import Provider
    from libcloud.compute.providers import get_driver
    import libcloud.security
except ImportError:
    raise ex.excInitError("apache-libcloud module must be installed")

class Cloud(rcCloud.Cloud):
    mode = 'amazon'

    def __init__(self, s, auth):
        rcCloud.Cloud.__init__(self, s, auth)
        if 'access_key_id' not in auth:
            raise ex.excInitError("option 'access_key_id' is mandatory in amazon section")
        if 'provider' not in auth:
            raise ex.excInitError("option 'provider' is mandatory in amazon section")
        if 'secret_key' not in auth:
            raise ex.excInitError("option 'secret_key' is mandatory in amazon section")
        o = get_driver(auth['provider'])
        self.driver = o(auth['access_key_id'], auth['secret_key'])
        if 'proxy' in auth:
            self.driver.connection.set_http_proxy(proxy_url=auth['proxy'])

    def app_id(self, svcname):
        return svcname.rstrip(self.auth['manager']).split('.')[-2]

    def cloud_id(self):
        return self.auth['provider']

    def app_cloud_id(self):
        return self.cloud_id()

    def list_svcnames(self):
        l = []
        return l

 0707010005c91b000081b40000000000000000000000015ab2175400000548000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/rcCloudGandi.py    import rcCloud
import rcExceptions as ex
import socket

try:
    from libcloud.compute.types import Provider
    from libcloud.compute.providers import get_driver
    import libcloud.security
except ImportError:
    raise ex.excInitError("apache-libcloud module must be installed")

class Cloud(rcCloud.Cloud):
    mode = 'gandi'

    def __init__(self, s, auth):
        rcCloud.Cloud.__init__(self, s, auth)
        if 'key' not in auth:
            raise ex.excInitError("option 'key' is mandatory in gandi section")
        gandi = get_driver(Provider.GANDI)
        try:
            self.driver = gandi(auth['key'])
        except Exception as e:
            raise ex.excInitError("error login to gandi cloud %s: %s"%(s, str(e)))

    def app_id(self):
        return ''

    def cloud_id(self):
        return mode

    def app_cloud_id(self):
        return mode

    def list_svcnames(self):
        l = []
        _id = self.app_cloud_id()
        try:
            vapps = self.driver.list_nodes()
        except socket.error as e:
            raise ex.excExecError("error connecting to %s cloud manager"%s)
        for vapp in vapps:
            __id = '.'.join((vapp.name, _id))
            for vm in vapp.extra['vms']:
                svcname = '.'.join((vm['name'], __id))
                l.append((vm['name'], svcname))
        return l

0707010005c91c000081b40000000000000000000000015ab2175400000868000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/rcCloudOpenstack.py    import rcCloud
import rcExceptions as ex
import socket

try:
    from libcloud.compute.types import Provider
    from libcloud.compute.providers import get_driver
    import libcloud.security
except ImportError:
    raise ex.excInitError("apache-libcloud module must be installed")

class Cloud(rcCloud.Cloud):
    mode = 'openstack'

    def __init__(self, s, auth):
        rcCloud.Cloud.__init__(self, s, auth)
        kwargs = {}

        if 'username' not in auth:
            raise ex.excInitError("option 'username' is mandatory in %s section"%self.mode)

        if 'password' not in auth:
            raise ex.excInitError("option 'password' is mandatory in %s section"%self.mode)

        if 'url' not in auth:
            raise ex.excInitError("option 'url' is mandatory in %s section"%self.mode)

        kwargs['ex_force_auth_url'] = auth['url']

        if 'tenant' in auth:
            self.tenant_name = auth['tenant']
            kwargs['ex_tenant_name'] = auth['tenant']
        else:
            self.tenant_name = None

        if 'version' in auth:
            kwargs['ex_force_auth_version'] = auth['version']
        else:
            kwargs['ex_force_auth_version'] = '2.0_password'

        if 'service_name' in auth:
            kwargs['ex_force_service_name'] = auth['service_name']

        if 'verify_ssl_cert' in auth and not auth['verify_ssl_cert']:
            libcloud.security.VERIFY_SSL_CERT = False

        openstack = get_driver(Provider.OPENSTACK)
        self.driver = openstack( auth['username'], auth['password'], **kwargs)

    def app_id(self, svcname=None):
        return self.tenant_name

    def cloud_id(self):
        return self.auth['url'].split("/")[2].split(':')[0]

    def app_cloud_id(self):
        _id = []
        app_id = self.app_id()
        if app_id is not None:
            _id.append(app_id)
        _id.append(self.cloud_id())
        return '.'.join(_id)

    def list_svcnames(self):
        l = []
        _id = self.app_cloud_id()
        for node in self.list_nodes():
            svcname = '.'.join((node.name, _id))
            l.append((node.name, svcname))
        return l

0707010005c91d000081b40000000000000000000000015ab217540000076c000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/rcCloudVcloud.py   import rcCloud
import rcExceptions as ex
import socket

try:
    from libcloud.compute.types import Provider
    from libcloud.compute.providers import get_driver
    import libcloud.security
except ImportError:
    raise ex.excInitError("apache-libcloud module must be installed")

class Cloud(rcCloud.Cloud):
    mode = 'vcloud'

    def __init__(self, s, auth):
        rcCloud.Cloud.__init__(self, s, auth)
        if 'username' not in auth:
            raise ex.excInitError("option 'username' is mandatory in vcloud section")
        if 'password' not in auth:
            raise ex.excInitError("option 'password' is mandatory in vcloud section")
        if 'manager' not in auth:
            raise ex.excInitError("option 'manager' is mandatory in vcloud section")
        if 'api_version' not in auth:
            auth['api_version'] = '1.5'
        vcloud = get_driver(Provider.VCLOUD)
        self.driver = vcloud(auth['username'], auth['password'],
                             host=auth['manager'], api_version=auth['api_version'])

    def app_id(self, svcname):
        return svcname.rstrip(self.auth['manager']).split('.')[-2]

    def cloud_id(self):
        return self.auth['manager']

    def app_cloud_id(self):
        _id = []
        l = self.auth['username'].split('@')
        if len(l) == 2:
            _id.append(l[1])
        _id.append(self.auth['manager'])
        return '.'.join(_id)

    def list_svcnames(self):
        l = []
        _id = self.app_cloud_id()
        try:
            vapps = self.driver.list_nodes()
        except socket.error as e:
            raise ex.excExecError("error connecting to %s cloud manager"%s)
        for vapp in vapps:
            __id = '.'.join((vapp.name, _id))
            for vm in vapp.extra['vms']:
                svcname = '.'.join((vm['name'], __id))
                l.append((vm['name'], svcname))
        return l

0707010005c91e000081fd0000000000000000000000015ab2175400017b83000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/rcCollectorCli.py  # -*- coding: utf-8 -*-

from __future__ import print_function

import os
import sys
import stat
import json
import optparse
import shlex
import re
import copy

# issue19884 workaround (spurious heading '\033[1034h')
TERM = os.environ.get("TERM")
if TERM:
    del os.environ["TERM"]
    import readline
    os.environ["TERM"] = TERM

try:
    import ConfigParser
except ImportError:
    import configparser as ConfigParser
import atexit
import fnmatch
import rcExceptions as ex
from rcGlobalEnv import Storage

try:
    import requests
except:
    raise ex.excError("This feature requires the python requests module")

try:
    from requests.packages.urllib3.exceptions import InsecureRequestWarning
except ImportError:
    InsecureRequestWarning = None

# the collector api doc uses restructured text we'll have to print
# in the command help messages
import textwrap
try:
    import docutils.utils
    import docutils.parsers
    import docutils.parsers.rst
    has_docutils = True
except:
    has_docutils = False

from rcUtilities import bdecode
from rcColor import formatter

if sys.version_info[0] >= 3:
    raw_input = input

progname = "opensvc-cli"
homedir = os.path.expanduser("~")
api_cache_f = os.path.join(homedir, "."+progname+".api")
conf_f = os.path.join(homedir, "."+progname)
conf_section = "collector"
history_f = conf_f + "_history"
global path
path = "/"
api_cache = None

ls_info_default = {
    "filter": "id",
    "props": ["id"],
    "fmt": "%(id)-10s",
}
ls_info = {
  "": {
    "filter": "path",
    "props": ["path"],
    "fmt": "           %(path)s",
  },
  "action_queue": {
    "filter": "id",
    "props": ["id", "command"],
    "fmt": "%(id)-10s %(command)s",
  },
  "apps": {
    "filter": "app",
    "props": ["id", "app"],
    "fmt": "%(id)-10s %(app)s",
  },
  "nodes": {
    "filter": "nodename",
    "props": ["node_id", "nodename"],
    "fmt": "%(node_id)s %(nodename)s",
  },
  "services": {
    "filter": "svcname",
    "props": ["svc_id", "svcname"],
    "fmt": "%(svc_id)s %(svcname)s",
  },
  "rulesets": {
    "filter": "ruleset_name",
    "props": ["id", "ruleset_name"],
    "fmt": "%(id)-10s %(ruleset_name)s",
  },
  "modulesets": {
    "filter": "modset_name",
    "props": ["id", "modset_name"],
    "fmt": "%(id)-10s %(modset_name)s",
  },
  "users": {
    "filter": "email",
    "props": ["id", "email"],
    "fmt": "%(id)-10s %(email)s",
  },
  "groups": {
    "filter": "role",
    "props": ["id", "role"],
    "fmt": "%(id)-10s %(role)s",
  },
  "tags": {
    "filter": "tag_name",
    "props": ["tag_id", "tag_name"],
    "fmt": "%(tag_id)s %(tag_name)s",
  },
  "variables": {
    "filter": "var_name",
    "props": ["id", "var_name"],
    "fmt": "%(id)-10s %(var_name)s",
  },
  "modules": {
    "filter": "modset_mod_name",
    "props": ["id", "modset_mod_name"],
    "fmt": "%(id)-10s %(modset_mod_name)s",
  },
  "filters": {
    "filter": "f_label",
    "props": ["id", "f_label"],
    "fmt": "%(id)-10s %(f_label)s",
  },
  "filtersets": {
    "filter": "fset_name",
    "props": ["id", "fset_name"],
    "fmt": "%(id)-10s %(fset_name)s",
  },
}


#
# requests setup
#
try:
    requests.packages.urllib3.disable_warnings()
except:
    pass

class Cmd(object):
    # color codes
    PURPLE = '\033[95m'
    CYAN = '\033[96m'
    DARKCYAN = '\033[36m'
    BLUE = '\033[94m'
    GREEN = '\033[92m'
    YELLOW = '\033[93m'
    RED = '\033[91m'
    BOLD = '\033[1m'
    UNDERLINE = '\033[4m'
    END = '\033[0m'

    def __init__(self, cli=None):
        self.cli = cli
        self.options = Storage()
        self.options.format = cli.format if cli else None

    @formatter
    def print_content(self, s):
        data = json.loads(bdecode(s))
        if self.options.format == "json":
            return data
        if "info" in data:
            if isinstance(data["info"], list):
                infos = data["info"]
            else:
                infos = [data["info"]]
            for info in infos:
                print("Info:", info)
        if "error" in data:
            if isinstance(data["error"], list):
                errors = data["error"]
            else:
                errors = [data["error"]]
            for error in errors:
                print("Error:", error)
        if "data" in data:
            return data["data"]
        return ""

    def path_match_handlers(self, p):
        for a, l in self.cli.api_o.get().items():
            for d in l:
                if path_match_handler(p, d):
                    return True
        return False

    def path_match_handlers_or_parents(self, p):
        for a, l in self.cli.api_o.get().items():
            for d in l:
                if path_match_handler_or_parents(p, d):
                    return True
        return False

    def colorize(self, s, c=None):
        if c is None:
            return s
        return c + s + self.END

    def get_handler(self, action, p):
        for d in self.cli.api_o.get()[action]:
            if path_match_handler(p, d):
                return d

    def match(self, line):
        """
          Tells if a CLI command line is to be handled by this command class,
          using the first word as a telltale.
        """
        l = line.split()
        if len(l) == 0 or l[0] != self.command:
            return False
        return True

    def replace_params_in_path(self, candidates_path, words):
        d = {}
        if candidates_path.count("<") == 0:
            return candidates_path
        for i, w in enumerate(words):
            if i < len(words) - 1:
                next_word = words[i+1]
            else:
                next_word = None
            if w.startswith("--") and next_word is not None:
                d[w.lstrip("-")] = next_word
        p = copy.copy(candidates_path)
        new_p = copy.copy(candidates_path)
        while p.count("<") > 0:
            try:
                param = p[p.index("<")+1:p.index(">")]
                p = p[p.index(">")+1:]
                if param in d:
                    new_p = new_p.replace("<"+param+">", d[param])
            except:
                break
        return new_p

    def set_parser_options_from_cmdline(self, line):
        words = shlex.split(line)
        self.set_parser_options_from_words(words)

    def set_parser_options_from_words(self, words):
        if len(words) == 0:
            return
        _path = None
        for i, w in enumerate(words):
            if i == 0:
                # command word
                continue
            if i == 1 and not w.startswith("-"):
                _path = w
                break
            if i > 1 and not words[i-1].startswith("-"):
                _path = w
                break
        if _path is None:
            return
        try:
            self.set_parser_options(_path)
        except Exception as e:
            print(e)

    def candidates(self, pattern, words):
        candidates = []
        if hasattr(self, "candidates_path"):
            param = words[-1]
            if not param in self.candidates_path and len(words) > 2 and pattern != "":
                param = words[-2]
            candidates_path = self.candidates_path.get(param)
        else:
            candidates_path = None
        if candidates_path:
            candidates_path = self.replace_params_in_path(candidates_path, words)
            pattern = candidates_path + "/" + pattern
        else:
            self.set_parser_options_from_words(words)
            for o in self.parser.option_list:
                candidates += o._long_opts

        if pattern is None:
            pattern = ""
        elif pattern == ".." or pattern.endswith("/.."):
            pass
        elif pattern.startswith("/") and pattern.endswith("/"):
            pass
        elif pattern.count("/") == 0:
            pattern += "*"
        else:
            pattern = pattern[:pattern.rindex("/")+1]
        ls_data = self.ls_data(pattern)
        for e in ls_data:
            if e.startswith("OBJ"):
                candidate = e.replace("OBJ", "").strip()
                l = candidate.split()
                if len(l) > 1 and len(candidate) > 11:
                    # <id 10-char padded> <string>
                    candidate = candidate[11:]
                if candidates_path is None and pattern.count("/") > 0:
                    candidate = pattern.rstrip("/")+"/"+candidate
                candidates.append(candidate)
            elif candidates_path is None and e.startswith("API") and e != "API":
                candidate = e.split()[-1]
                if pattern.startswith("/") and not candidate.startswith("/"):
                    candidate = pattern + candidate
                candidates.append(candidate)
        return candidates

    def args_to_path(self, args):
        try:
            arg1 = args[1]
            if arg1.startswith("/"):
                _path = arg1
            else:
                _path = copy.copy(path) + "/" + args[1]
        except:
            _path = copy.copy(path)
        return _path

    def get_data_from_options(self, options):
        data = {}
        files = {}
        headers = None
        if options is None or "data" not in options.__dict__ or options.data is None:
            return data, files, headers
        for d in options.data:
            if len(d) > 0 and d[0] == "@" and os.path.exists(d[1:]):
                with open(d[1:], 'r') as fd:
                    data = fd.read()
                headers = {
                  'Accept' : 'application/json',
                  'Content-Type' : 'application/json; charset=utf-8'
                }
                return data, files, headers
            if d.count("=") == 0 or len(d) < d.index("=")+1:
                print("ignore malformated data:", d)
                continue
            key = d[:d.index("=")]
            val = d[d.index("=")+1:]
            if len(val) > 1 and val[0] == "@" and os.path.exists(val[1:]):
                fpath = val[1:]
                try:
                    fd = open(fpath, 'rb')
                except:
                    print("error opening file %s" % fpath, file=sys.stderr)
                    raise
                files["file"] = (os.path.realpath(fpath), fd)
            else:
                data[key] = val
        return data, files, headers

    def factorize_dot_dot(self, p):
        l1 = p.split("/")
        l2 = []
        for i, e in enumerate(l1):
            if i == 0:
                l2.append(e)
                continue
            if e == "..":
                l2.pop()
                continue
            l2.append(e)
        if l2 == [""]:
            p = "/"
        else:
            p = "/".join(l2)
        if len(p) > 1:
            p.rstrip("/")
        return p

    def ls_data(self, line):
        ls_data = []
        global path
        line = line.strip()

        # strip the ls command
        relpath = re.sub("^\s*ls\s+", "", line)
        if relpath == "ls":
            relpath = ""

        relpath = relpath.strip()
        if relpath == ".." or relpath.endswith("/.."):
            relpath += "/"

        p = get_fullpath(relpath)
        if p.count("/") == 0:
            relpath = ""
            raw_req_path = copy.copy(path)
            shell_pattern = p
        elif self.is_glob(p[p.rindex("/"):]):
            v = p.split("/")
            raw_req_path = "/".join(v[:-1])
            shell_pattern = v[-1]
            v = relpath.split("/")
            relpath = "/".join(v[:-1])
        else:
            raw_req_path = p
            shell_pattern = ""

        req_path = self.factorize_dot_dot(raw_req_path)
        sql_pattern = shell_pattern.replace("*", "%")
        sql_pattern = sql_pattern.replace("?", "_")

        last = req_path.rstrip("/").split("/")[-1]
        info = ls_info.get(last, ls_info_default)
        props = info.get("props", [])
        filter_prop = info.get("filter", "id")
        fmt = info.get("fmt", "%(id)s")

        if req_path not in ("/", "") and self.path_match_handlers(req_path):
            # object listing
            params = {
              "limit": 0,
              "meta": 0,
              "props": ",".join(props),
            }
            if len(sql_pattern) > 0:
                params["query"] = filter_prop + " like " + sql_pattern
            r = requests.get(self.cli.api+req_path, params=params, auth=self.cli.auth, verify=not self.cli.insecure)
            validate_response(r)
            data = json.loads(bdecode(r.content)).get("data")
            if type(data) == list:
                ls_data += map(lambda d: "OBJ " + fmt % d, data)

        if self.api_candidates:
            # api paths listing
            info = ls_info.get("", ls_info_default)
            fmt = info.get("fmt", "%(id)s")
            props = info.get("props", [])
            filter_prop = info.get("filter", [])
            data = [d for d in self.get_handler_paths() if path_match_handler_or_parents(req_path, d) and d["path"] != req_path]
            #data += path_children_api(req_path)
            if len(shell_pattern) > 0:
                if not shell_pattern.startswith("/"):
                    shell_pattern = req_path + "/" + shell_pattern
                    shell_pattern = shell_pattern.replace("//", "/")
                data = [d for d in data if fnmatch.fnmatch(d.get(filter_prop), shell_pattern)]
            for i, d in enumerate(data):
                data[i]["path"] = re.sub("^"+req_path, relpath, d["path"])

            for d in data:
                line = "API "
                for action in ("GET", "POST", "DELETE", "PUT"):
                    if action in d["actions"]:
                        line += action
                    else:
                        line += "."*len(action)
                    line += " "
                line += fmt %d
                ls_data.append(line)
        return ls_data

    def get_handler_paths(self):
        data = self.cli.api_o.get()
        all_handlers = {}
        for action, l in data.items():
            for h in l:
                if h["path"] not in all_handlers:
                    h["actions"] = [action]
                    all_handlers[h["path"]] = h
                else:
                    all_handlers[h["path"]]["actions"].append(action)
        return [all_handlers[p] for p in sorted(all_handlers.keys())]

    def is_glob(self, s):
        if len(set(s) & set("?*[")) > 0:
            return True
        return False

    def set_parser_options(self, _path):
        if not _path.startswith("/"):
            _path = os.path.join(path, _path)
        try:
            h = self.get_handler(self.command.upper(), _path)
        except Exception as e:
            return
        if h is None:
            return self.parser
        for o in self.parser._get_all_options()[1:]:
            self.parser.remove_option(str(o))
        if hasattr(self, "parser_options"):
            for o in self.parser_options:
                self.parser.add_option(o)
        for param, d in h["params"].items():
            if d.get("type") == "list":
                action = "append"
                default = []
            else:
                action = "append"
                default = None
            self.parser.add_option("--"+param, default=default, action=action, dest=param, help=d["desc"])

class IndentedHelpFormatterRst(optparse.IndentedHelpFormatter):
    def format_description(self, description):
        if not has_docutils:
            return description

        if not description:
            return ""

        doc = docutils.utils.new_document("foo")
        doc.settings.tab_width = 4
        doc.settings.pep_references = None
        doc.settings.rfc_references = None
        p = docutils.parsers.rst.Parser()
        p.parse(description, doc)
        description = doc.astext()

        desc_width = self.width - self.current_indent
        indent = " "*self.current_indent

        # the above is still the same
        bits = description.split('\n')
        formatted_bits = [
          textwrap.fill(bit, desc_width, initial_indent=indent, subsequent_indent=indent)
          for bit in bits
        ]
        result = "\n".join(formatted_bits) + "\n"
        return result

    def format_option(self, option):
        result = []
        opts = self.option_strings[option]
        opt_width = self.help_position - self.current_indent - 2
        if len(opts) > opt_width:
            opts = "%*s%s\n" % (self.current_indent, "", opts)
            indent_first = self.help_position
        else: # start help on same line as opts
            opts = "%*s%-*s  " % (self.current_indent, "", opt_width, opts)
            indent_first = 0
        result.append(opts)
        if option.help:
            help_text = self.expand_default(option)
            # Everything is the same up through here
            help_lines = []
            for para in help_text.split("\n"):
                help_lines.extend(textwrap.wrap(para, self.help_width))
            # Everything is the same after here
            result.append("%*s%s\n" % (indent_first, "", help_lines[0]))
            result.extend(["%*s%s\n" % (self.help_position, "", line) for line in help_lines[1:]])
        elif opts[-1] != "\n":
            result.append("\n")
        return "".join(result)


class CliError(Exception):
    pass

class Api(object):
    api_cache = None

    def __init__(self, cli=None, refresh=False):
        self.cli = cli
        self.load(refresh=refresh)

    def load(self, refresh=False):
        if not refresh and os.path.exists(api_cache_f):
            # try local cache first
            try:
                with open(api_cache_f, 'r') as f:
                    self.api_cache = json.loads(f.read())
                return
            except Exception as e:
                print(e)
                os.unlink(api_cache_f)

        # fallback to fetching the cache
        print("load api cache", file=sys.stderr)
        r = requests.get(self.cli.api, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        try:
            self.api_cache = json.loads(bdecode(r.content))["data"]
        except:
            raise CliError(r.content)

        # update local cache
        with open(api_cache_f, 'w') as f:
            f.write(json.dumps(self.api_cache, indent=4))

    def get(self):
        if self.api_cache:
            return copy.deepcopy(self.api_cache)
        self.load()
        return copy.deepcopy(self.api_cache)

class OptionParsingError(RuntimeError):
    def __init__(self, msg):
        self.msg = msg
    def __str__(self):
        return self.msg

class OptionParsingExit(Exception):
    def __init__(self, status, msg):
        self.msg = msg
        self.status = status
    def __str__(self):
        return self.msg

class CmdOptionParser(optparse.OptionParser):
    def __init__(self, *args, **vars):
        vars["formatter"] = IndentedHelpFormatterRst()
        optparse.OptionParser.__init__(self, *args, **vars)

    def error(self, msg):
        raise OptionParsingError(msg)

    def exit(self, status=0, msg=None):
        raise OptionParsingExit(status, msg)

def get_fullpath(relpath):
    if relpath.startswith("/"):
        return relpath
    if relpath == "":
        return path
    return path+"/"+relpath

class CmdHelp(Cmd):
    command = "help"
    desc = "Print this help message."
    parser = CmdOptionParser(description=desc)

    def cmd(self, line):
        from textwrap import TextWrapper
        wrapper = TextWrapper(initial_indent="    ", subsequent_indent="    ", width=78)
        commands_h = {}
        for c in self.cli.commands:
            commands_h[c.command] = c
        base_commands = sorted(commands_h.keys())
        for command in base_commands:
            c = commands_h[command]
            print(c.command)
            print()
            if hasattr(c, "desc"):
                print(wrapper.fill(c.desc))
                print()

class CmdLs(Cmd):
    api_candidates = True
    command = "ls"
    desc = "List the API handlers and available objects matching the given pattern."
    parser = CmdOptionParser(description=desc)

    def cmd(self, line):
        ls_data = self.ls_data(line)
        for s in ls_data:
            print(s)

class CmdDelete(Cmd):
    api_candidates = True
    command = "delete"
    desc = "Execute a DELETE request on the given API handler."
    parser = CmdOptionParser(description=desc)
    parser_options = [
      optparse.make_option("--data", default=None, action="append", dest="data",
                           help="A key=value pair to filter the deleted data. Multiple --data can be specified.")
    ]

    def cmd(self, line):
        self.set_parser_options_from_cmdline(line)
        global path
        try:
            options, args = self.parser.parse_args(args=shlex.split(line))
        except Exception as e:
            try: print(e)
            except: pass
            return
        data, files, headers = self.get_data_from_options(options)
        params = {}
        if 'query' in options.__dict__ and options.query is not None:
            params["query"] = options.query
        _path = self.args_to_path(args)
        r = requests.delete(self.cli.api+_path, params=params, data=data, headers=headers, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

class CmdPost(Cmd):
    api_candidates = True
    command = "post"
    desc = "Execute a POST request on the given API handler. The data can be set using --data."
    parser = CmdOptionParser(description=desc)
    parser_options = [
      optparse.make_option("--data", default=None, action="append", dest="data",
                           help="A key=value pair to include in the post data. Multiple --data can be specified.")
    ]

    def cmd(self, line):
        self.set_parser_options_from_cmdline(line)
        global path
        try:
            options, args = self.parser.parse_args(args=shlex.split(line))
        except Exception as e:
            try: print(e)
            except: pass
            return
        data, files, headers = self.get_data_from_options(options)
        params = {}
        if 'query' in options.__dict__ and options.query is not None:
            params["query"] = options.query
        _path = self.args_to_path(args)
        r = requests.post(self.cli.api+_path, data=data, files=files, params=params, headers=headers, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

class CmdPut(Cmd):
    api_candidates = True
    command = "put"
    desc = "Execute a PUT request on the given API handler. The data can be set using --data."
    parser = CmdOptionParser(description=desc)
    parser_options = [
      optparse.make_option("--data", default=None, action="append", dest="data",
                           help="A key=value pair to include in the post data. Multiple --data can be specified.")
    ]

    def cmd(self, line):
        self.set_parser_options_from_cmdline(line)
        global path
        try:
            options, args = self.parser.parse_args(args=shlex.split(line))
        except Exception as e:
            try: print(e)
            except: pass
            return
        data, files, headers = self.get_data_from_options(options)
        _path = self.args_to_path(args)
        r = requests.put(self.cli.api+_path, data=data, files=files, headers=headers, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

class CmdSafe(Cmd):
    command = "safe"
    desc = "Upload, download and manage files in the collector safe. The safe is a file sharing facility with access control rules for nodes and users making it suitable to serve compliance reference files."
    parser = CmdOptionParser(description=desc)
    parser.add_option("--ls", default=None, action="store_true", dest="ls",
                      help="List the accessible files in the safe.")
    parser.add_option("--upload", default=None, action="store_true", dest="upload",
                      help="Upload the file pointed by --file to the safe. Optionally give a name using --name.")
    parser.add_option("--download", default=None, action="store_true", dest="download",
                      help="Download from the safe the file pointed by --file to the file path or directory pointed by --to.")
    parser.add_option("--file", default=None, action="store", dest="file",
                      help="The safe file uuid to download, or the local file to upload.")
    parser.add_option("--to", default=None, action="store", dest="to",
                      help="The local file path or directory name to download.")
    parser.add_option("--name", default=None, action="store", dest="name",
                      help="The user-friendly name to attach to the upload.")
    candidates_path = {
      "--file": "/safe",
    }
    api_candidates = False

    def cmd(self, line):
        global path
        try:
            options, args = self.parser.parse_args(args=shlex.split(line))
        except Exception as e:
            try: print(e)
            except: pass
            return
        self.ls(options)
        self.upload(options)
        self.download(options)

    def ls(self, options):
        if options.ls is None:
            return
        params = {}
        r = requests.get(self.cli.api+"/safe", params=params, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def upload(self, options):
        if options.upload is None:
            return
        if options.file is None:
            raise CliError("--file is mandatory for --upload")
        data = {}
        if options.name:
            data["name"] = options.name
        if not os.path.exists(options.file):
            raise CliError("%s file not found" % options.file)

        files = {
          "file": (os.path.realpath(options.file), open(options.file, 'rb')),
        }

        r = requests.post(self.cli.api+"/safe/upload", data=data, files=files, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def download(self, options):
        if options.download is None:
            return
        if options.file is None:
            raise CliError("--file is mandatory for --download")
        if options.to is None:
            raise CliError("--to is mandatory for --download")

        if os.path.exists(options.to) and os.path.isdir(options.to):
            to = os.path.join(options.to, options.file)
        else:
            to = options.to

        r = requests.get(self.cli.api+"/safe/"+options.file+"/download", stream=True, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)

        if not r.ok:
            try:
                d = json.loads(bdecode(r.content))
                print(d["error"], file=sys.stderr)
                return
            except:
                pass
            raise CliError("download failed")

        with open(options.to, 'wb') as f:
            pass
        os.chmod(options.to, 0o0600)
        with open(options.to, 'wb') as f:
            for block in r.iter_content(1024):
                print(".")
                f.write(block)

        print("downloaded")



class CmdSysreport(Cmd):
    command = "sysreport"
    desc = "Show sysreport information"
    parser = CmdOptionParser(description=desc)
    parser.add_option("--log", default=None, action="store_true", dest="log",
                      help="")
    parser.add_option("--begin", default=None, action="store", dest="begin",
                      help="The sysreport analysis begin date.")
    parser.add_option("--end", default=None, action="store", dest="end",
                      help="The sysreport analysis begin date.")
    parser.add_option("--path", default=None, action="store", dest="path",
                      help="A path globing pattern to limit the sysreport analysis to.")
    parser.add_option("--node", default=None, action="store", dest="node",
                      help="The sysreport node name.")
    parser.add_option("--cid", default=None, action="store", dest="cid",
                      help="The commit id to show as diff. This cid is displayed in the summary listing obtained by the --log action without specifying --cid.")
    candidates_path = {
      "--node": "/nodes",
    }
    api_candidates = False

    def cmd(self, line):
        global path
        try:
            options, args = self.parser.parse_args(args=shlex.split(line))
        except Exception as e:
            try: print(e)
            except: pass
            return
        self.log(options)
        self.log_cid(options)

    def print_log(self, data):
        for d in data["data"]:
            print(self.colorize("cid: %s" % d["cid"], c=self.DARKCYAN))
            print(self.colorize("change detection date: %s" % d["start"].replace("T", " "), c=self.GREEN))
            if "summary" in d:
                print()
                print("  "+d["summary"])
            print()
            for fpath in d["stat"]:
                print("  "+fpath)
            print()

    def print_log_cid(self, data):
        for fpath, d in data["data"]["blocks"].items():
            print(self.colorize("path: " + fpath, c=self.DARKCYAN))
            if d["secure"]:
                print(self.colorize("visible: by node responsibles", c=self.DARKRED))
            else:
                print(self.colorize("visible: by everyone", c=self.DARKCYAN))
            print()
            for line in d["diff"].split("\n"):
                if line.startswith("-"):
                    c = self.RED
                elif line.startswith("+"):
                    c = self.GREEN
                else:
                    c = None
                print(self.colorize(line, c=c))
            print()

    def log(self, options):
        if options.log is None or options.node is None or options.cid is not None:
            return
        params = {}
        if options.begin:
            params["begin"] = options.begin
        if options.end:
            params["end"] = options.end
        if options.path:
            params["path"] = options.path
        r = requests.get(self.cli.api+"/nodes/"+options.node+"/sysreport", params=params, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        data = json.loads(bdecode(r.content))
        self.print_log(data)

    def log_cid(self, options):
        if options.log is None or options.node is None or options.cid is None:
            return
        params = {}
        if options.path:
            params["path"] = options.path
        r = requests.get(self.cli.api+"/nodes/"+options.node+"/sysreport/"+options.cid, params=params, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        data = json.loads(bdecode(r.content))
        self.print_log_cid(data)


class CmdFilter(Cmd):
    command = "filter"
    desc = "Handle design actions on a filter"
    parser = CmdOptionParser(description=desc)
    parser.add_option("--list", default=None, action="store_true", dest="list",
                      help="List filters")
    parser.add_option("--show", default=None, action="store_true", dest="show",
                      help="Show a filter design")
    parser.add_option("--create", default=None, action="store_true", dest="create",
                      help="Create a filter")
    parser.add_option("--delete", default=None, action="store_true", dest="delete",
                      help="Delete a filter")
    parser.add_option("--set", default=None, action="store_true", dest="set",
                      help="Set filter properties")
    parser.add_option("--attach", default=None, action="store_true", dest="attach",
                      help="Attach a filter to the filterset pointed by --filterset")
    parser.add_option("--detach", default=None, action="store_true", dest="detach",
                      help="Detach a filter from the filterset pointed by --filterset")
    parser.add_option("--filter", default=None, action="store", dest="filter",
                      help="The name or id of the filter to manage")
    parser.add_option("--filterset", default=None, action="store", dest="filterset",
                      help="The name or id of the filterset to attach to or detach from")
    parser.add_option("--value", default=None, action="store", dest="value",
                      help="with --set or --create, set the filter value parameter")
    parser.add_option("--operator", default=None, action="store", dest="operator",
                      help="with --set or --create, set the filter operator parameter. Accepted operators: =, <, >, <=, >=, LIKE, IN")
    parser.add_option("--field", default=None, action="store", dest="field",
                      help="with --set or --create, set the filter field parameter")
    parser.add_option("--table", default=None, action="store", dest="table",
                      help="with --set or --create, set the filter table parameter")
    parser.add_option("--order", default=None, action="store", dest="order",
                      help="with --attach, set the filter attachment order parameter. Integer.")
    parser.add_option("--logical-operator", default=None, action="store", dest="logical_operator",
                      help="with --attach, set the filter attachment logical operator parameter. Accepted operators: AND, OR, AND NOT, OR NOT")
    candidates_path = {
      "--filterset": "/filtersets",
      "--filter": "/filters",
    }
    api_candidates = False

    def cmd(self, line):
        global path
        try:
            options, args = self.parser.parse_args(args=shlex.split(line))
        except Exception as e:
            try: print(e)
            except: pass
            return
        if options.filter:
            options.filter = options.filter.replace("%", "(percent)")
        self.list_filters(options)
        self.show_filter(options)
        self.create_filter(options)
        self.delete_filter(options)
        self.set_filter(options)
        self.attach_filter_to_filterset(options)
        self.detach_filter_from_filterset(options)

    def list_filters(self, options):
        if options.list is None:
            return
        p = "/filters"
        o = CmdLs(self.cli).cmd(p)

    def show_filter(self, options):
        if options.show is None or options.filter is None:
            return
        o = CmdShow(self.cli)
        data = o.get_data("/filters/"+options.filter)
        o.print_filter(options.filter, data)

    def create_filter(self, options):
        if options.create is None:
            return
        global path
        _path = "/filters"
        data = {
          "f_table": options.table,
          "f_field": options.field,
          "f_op": options.operator,
          "f_value": options.value,
        }
        r = requests.post(self.cli.api+_path, data=data, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def delete_filter(self, options):
        if options.delete is None or options.filter is None:
            return
        global path
        _path = "/filters/"+options.filter
        r = requests.delete(self.cli.api+_path, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def attach_filter_to_filterset(self, options):
        if options.attach is None or options.filterset is None or options.filter is None:
            return
        _path = "/filtersets/%s/filters/%s" % (options.filterset, options.filter)
        data = {}
        if options.logical_operator:
            data["f_log_op"] = options.logical_operator
        if options.logical_operator:
            data["f_order"] = options.order
        r = requests.post(self.cli.api+_path, data=data, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def detach_filter_from_filterset(self, options):
        if options.detach is None or options.filterset is None or options.filter is None:
            return
        _path = "/filtersets/%s/filters/%s" % (options.filterset, options.filter)
        r = requests.delete(self.cli.api+_path, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def set_filter(self, options):
        if options.set is None or options.filter is None:
            return
        data = {}
        if options.table is not None:
            data["f_table"] = options.table
        if options.operator is not None:
            data["f_op"] = options.operator
        if options.table is not None:
            data["f_field"] = options.field
        if options.value is not None:
            data["f_value"] = options.value
        if len(data) == 0:
            return
        _path = "/filters/%s" % options.filter
        r = requests.post(self.cli.api+_path, data=data, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)


class CmdFilterset(Cmd):
    command = "filterset"
    desc = "Handle design actions on a filterset"
    parser = CmdOptionParser(description=desc)
    parser.add_option("--list", default=None, action="store_true", dest="list",
                      help="List filtersets")
    parser.add_option("--show", default=None, action="store_true", dest="show",
                      help="Show a filterset design, with nesting")
    parser.add_option("--create", default=None, action="store_true", dest="create",
                      help="Create a filterset")
    parser.add_option("--delete", default=None, action="store_true", dest="delete",
                      help="Delete a filterset")
    parser.add_option("--set", default=None, action="store_true", dest="set",
                      help="Set filterset properties")
    parser.add_option("--attach", default=None, action="store_true", dest="attach",
                      help="Attach a filterset to the filterset pointed by --parent-filterset")
    parser.add_option("--detach", default=None, action="store_true", dest="detach",
                      help="Detach a filterset from the filterset pointed by --parent-filterset")
    parser.add_option("--rename", default=None, action="store_true", dest="rename",
                      help="Rename a filterset")
    parser.add_option("--filterset", default=None, action="store", dest="filterset",
                      help="The name or id of the filterset to manage")
    parser.add_option("--parent-filterset", default=None, action="store", dest="parent_filterset",
                      help="The name or id of the filterset to attach to or detach from")
    parser.add_option("--to", default=None, action="store", dest="to",
                      help="with --rename, set the new filterset name")
    parser.add_option("--stats", default=None, action="store_true", dest="stats",
                      help="with --set, set the filterset stats parameter to true")
    parser.add_option("--not-stats", default=None, action="store_false", dest="stats",
                      help="with --set, set the filterset stats parameter to false")
    candidates_path = {
      "--to": "/filtersets",
      "--filterset": "/filtersets",
      "--parent-filterset": "/filtersets",
    }
    api_candidates = False

    def cmd(self, line):
        global path
        try:
            options, args = self.parser.parse_args(args=shlex.split(line))
        except Exception as e:
            try: print(e)
            except: pass
            return
        self.list_filtersets(options)
        self.show_filterset(options)
        self.create_filterset(options)
        self.delete_filterset(options)
        self.set_filterset(options)
        self.attach_filterset_to_filterset(options)
        self.detach_filterset_from_filterset(options)
        self.rename_filterset(options)

    def list_filtersets(self, options):
        if options.list is None:
            return
        p = "/filtersets"
        o = CmdLs(self.cli).cmd(p)

    def show_filterset(self, options):
        if options.show is None or options.filterset is None:
            return
        o = CmdShow(self.cli)
        data = o.get_data("/filtersets/"+options.filterset)
        o.print_filterset(options.filterset, data)

    def create_filterset(self, options):
        if options.create is None or options.filterset is None:
            return
        global path
        _path = "/filtersets"
        data = {
          "fset_name": options.filterset,
        }
        r = requests.post(self.cli.api+_path, data=data, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def delete_filterset(self, options):
        if options.delete is None or options.filterset is None:
            return
        global path
        _path = "/filtersets/"+options.filterset
        r = requests.delete(self.cli.api+_path, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def attach_filterset_to_filterset(self, options):
        if options.attach is None or options.parent_filterset is None or options.filterset is None:
            return
        _path = "/filtersets/%s/filtersets/%s" % (options.parent_filterset, options.filterset)
        r = requests.post(self.cli.api+_path, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def detach_filterset_from_filterset(self, options):
        if options.detach is None or options.parent_filterset is None or options.filterset is None:
            return
        _path = "/filtersets/%s/filtersets/%s" % (options.parent_filterset, options.filterset)
        r = requests.delete(self.cli.api+_path, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def rename_filterset(self, options):
        if options.rename is None or options.filterset is None or options.to is None:
            return
        data = {
          "fset_name": options.to,
        }
        _path = "/filtersets/%s" % options.filterset
        r = requests.post(self.cli.api+_path, data=data, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def set_filterset(self, options):
        if options.set is None or options.filterset is None:
            return
        self.set_filterset_stats(options)

    def set_filterset_stats(self, options):
        if options.stats is None:
            return
        data = {
          "fset_stats": options.stats,
        }
        _path = "/filtersets/%s" % options.filterset
        r = requests.post(self.cli.api+_path, data=data, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)


class CmdModuleset(Cmd):
    command = "moduleset"
    desc = "Handle design actions on a compliance moduleset"
    parser = CmdOptionParser(description=desc)
    parser.add_option("--list", default=None, action="store_true", dest="list",
                      help="List modulesets")
    parser.add_option("--show", default=None, action="store_true", dest="show",
                      help="Show a moduleset design, with nesting")
    parser.add_option("--clone", default=None, action="store_true", dest="clone",
                      help="Clone a moduleset, including modules, moduleset-moduleset and moduleset-ruleset relations. Reset the publication as responsible groups.")
    parser.add_option("--create", default=None, action="store_true", dest="create",
                      help="Create a moduleset")
    parser.add_option("--delete", default=None, action="store_true", dest="delete",
                      help="Delete a moduleset")
    parser.add_option("--attach", default=None, action="store_true", dest="attach",
                      help="Attach the moduleset to a moduleset")
    parser.add_option("--detach", default=None, action="store_true", dest="detach",
                      help="Detach the moduleset from a moduleset")
    parser.add_option("--rename", default=None, action="store_true", dest="rename",
                      help="Rename a moduleset")
    parser.add_option("--moduleset", default=None, action="store", dest="moduleset",
                      help="The name or id of the moduleset to manage")
    parser.add_option("--parent-moduleset", default=None, action="store", dest="parent_moduleset",
                      help="The name or id of the moduleset to attach to or detach from")
    parser.add_option("--publication-group", default=None, action="store", dest="publication_group",
                      help="The name or id of the group to attach or detach as publication")
    parser.add_option("--responsible-group", default=None, action="store", dest="responsible_group",
                      help="The name or id of the group to attach or detach as responsible")
    parser.add_option("--to", default=None, action="store", dest="to",
                      help="with --rename, set the new moduleset name")
    candidates_path = {
      "--moduleset": "/compliance/modulesets",
      "--parent-moduleset": "/compliance/modulesets",
      "--publication-group": "/groups",
      "--responsible-group": "/groups",
    }
    api_candidates = False

    def cmd(self, line):
        global path
        try:
            options, args = self.parser.parse_args(args=shlex.split(line))
        except Exception as e:
            try: print(e)
            except: pass
            return
        self.list_modulesets(options)
        self.clone_moduleset(options)
        self.show_moduleset(options)
        self.create_moduleset(options)
        self.delete_moduleset(options)
        self.attach_publication_group_to_moduleset(options)
        self.detach_publication_group_from_moduleset(options)
        self.attach_responsible_group_to_moduleset(options)
        self.detach_responsible_group_from_moduleset(options)
        self.attach_moduleset_to_moduleset(options)
        self.detach_moduleset_from_moduleset(options)
        self.rename_moduleset(options)

    def list_modulesets(self, options):
        if options.list is None:
            return
        p = "/compliance/modulesets"
        o = CmdLs(self.cli).cmd(p)

    def show_moduleset(self, options):
        if options.show is None:
            return
        o = CmdShow(self.cli)
        data = o.get_data("/compliance/modulesets/"+options.moduleset)
        o.print_moduleset(options.moduleset, data)

    def create_moduleset(self, options):
        if options.create is None or options.moduleset is None:
            return
        global path
        _path = "/compliance/modulesets"
        data = {
          "modset_name": options.moduleset,
        }
        r = requests.post(self.cli.api+_path, data=data, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def clone_moduleset(self, options):
        if options.clone is None or options.moduleset is None:
            return
        data = {
          "action": "clone",
        }
        _path = "/compliance/modulesets/%s" % options.moduleset
        r = requests.put(self.cli.api+_path, data=data, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def delete_moduleset(self, options):
        if options.delete is None or options.moduleset is None:
            return
        global path
        _path = "/compliance/modulesets/"+options.moduleset
        r = requests.delete(self.cli.api+_path, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def attach_publication_group_to_moduleset(self, options):
        if options.attach is None or options.publication_group is None or options.moduleset is None:
            return
        _path = "/compliance/modulesets/%s/publications/%s" % (options.moduleset, options.publication_group)
        r = requests.post(self.cli.api+_path, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def attach_responsible_group_to_moduleset(self, options):
        if options.attach is None or options.responsible_group is None or options.moduleset is None:
            return
        _path = "/compliance/modulesets/%s/responsibles/%s" % (options.moduleset, options.responsible_group)
        r = requests.post(self.cli.api+_path, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def detach_publication_group_from_moduleset(self, options):
        if options.detach is None or options.publication_group is None or options.moduleset is None:
            return
        _path = "/compliance/modulesets/%s/publications/%s" % (options.moduleset, options.publication_group)
        r = requests.delete(self.cli.api+_path, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def detach_responsible_group_from_moduleset(self, options):
        if options.detach is None or options.responsible_group is None or options.moduleset is None:
            return
        _path = "/compliance/modulesets/%s/responsibles/%s" % (options.moduleset, options.responsible_group)
        r = requests.delete(self.cli.api+_path, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def attach_moduleset_to_moduleset(self, options):
        if options.attach is None or options.parent_moduleset is None or options.moduleset is None:
            return
        _path = "/compliance/modulesets/%s/modulesets/%s" % (options.parent_moduleset, options.moduleset)
        r = requests.post(self.cli.api+_path, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def detach_moduleset_from_moduleset(self, options):
        if options.detach is None or options.parent_moduleset is None or options.moduleset is None:
            return
        _path = "/compliance/modulesets/%s/modulesets/%s" % (options.parent_moduleset, options.moduleset)
        r = requests.delete(self.cli.api+_path, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def rename_moduleset(self, options):
        if options.rename is None or options.moduleset is None or options.to is None:
            return
        data = {
          "modset_name": options.to,
        }
        _path = "/compliance/modulesets/%s" % options.moduleset
        r = requests.post(self.cli.api+_path, data=data, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)


class CmdModule(Cmd):
    command = "module"
    desc = "Handle design actions on a compliance module"
    parser = CmdOptionParser(description=desc)
    parser.add_option("--list", default=None, action="store_true", dest="list",
                      help="List a moduleset modules")
    parser.add_option("--add", default=None, action="store_true", dest="add",
                      help="Add a module to a moduleset")
    parser.add_option("--remove", default=None, action="store_true", dest="remove",
                      help="Remove a module from a moduleset")
    parser.add_option("--set", default=None, action="store_true", dest="set",
                      help="Set module properties")
    parser.add_option("--rename", default=None, action="store_true", dest="rename",
                      help="Rename a module")
    parser.add_option("--module", default=None, action="store", dest="module",
                      help="The name or id of the module")
    parser.add_option("--moduleset", default=None, action="store", dest="moduleset",
                      help="The name or id of the module's moduleset")
    parser.add_option("--autofix", default=None, action="store_true", dest="autofix",
                      help="with --set, set the autofix property to true")
    parser.add_option("--not-autofix", default=None, action="store_false", dest="autofix",
                      help="with --set, set the autofix property to false")
    parser.add_option("--to", default=None, action="store", dest="to",
                      help="with --rename, set the new module name")
    candidates_path = {
      "--moduleset": "/compliance/modulesets",
      "--module": "/compliance/modulesets/<moduleset>/modules",
      "--to": "/compliance/modulesets/<moduleset>/modules",
    }
    api_candidates = False

    def cmd(self, line):
        global path
        try:
            options, args = self.parser.parse_args(args=shlex.split(line))
        except Exception as e:
            try: print(e)
            except: pass
            return
        self.list_modules(options)
        self.add_module(options)
        self.remove_module(options)
        self.set_module(options)
        self.rename_module(options)

    def list_modules(self, options):
        if options.list is None or options.moduleset is None:
            return
        p = "/compliance/modulesets/%s/modules" % options.moduleset
        o = CmdLs(self.cli).cmd(p)

    def add_module(self, options):
        if options.add is None or options.moduleset is None or options.module is None:
            return
        global path
        _path = "/compliance/modulesets/%s/modules" % options.moduleset
        data = {
          "modset_mod_name": options.module,
        }
        r = requests.post(self.cli.api+_path, data=data, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def remove_module(self, options):
        if options.remove is None or options.moduleset is None or options.module is None:
            return
        global path
        _path = "/compliance/modulesets/%s/modules/%s" % (options.moduleset, options.module)
        r = requests.delete(self.cli.api+_path, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def rename_module(self, options):
        if options.rename is None or options.moduleset is None or options.module is None or options.to is None:
            return
        data = {
          "modset_mod_name": options.to,
        }
        _path = "/compliance/modulesets/%s/modules/%s" % (options.moduleset, options.module)
        r = requests.post(self.cli.api+_path, data=data, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)


    def set_module(self, options):
        if options.set is None or options.moduleset is None or options.module is None:
            return
        self.set_module_autofix(options)

    def set_module_autofix(self, options):
        if options.autofix is None:
            return
        data = {
          "autofix": options.autofix,
        }
        _path = "/compliance/modulesets/%s/modules/%s" % (options.moduleset, options.module)
        r = requests.post(self.cli.api+_path, data=data, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

class CmdVariable(Cmd):
    command = "variable"
    desc = "Handle design actions on a compliance variable"
    parser = CmdOptionParser(description=desc)
    parser.add_option("--list", default=None, action="store_true", dest="list",
                      help="List variables in a ruleset")
    parser.add_option("--add", default=None, action="store_true", dest="add",
                      help="Add a variable to a ruleset")
    parser.add_option("--remove", default=None, action="store_true", dest="remove",
                      help="Remove a variable from a ruleset")
    parser.add_option("--copy", default=None, action="store_true", dest="copy",
                      help="Copy a variable to another ruleset")
    parser.add_option("--move", default=None, action="store_true", dest="move",
                      help="Move a variable to another ruleset")
    parser.add_option("--set", default=None, action="store_true", dest="set",
                      help="Set variable properties")
    parser.add_option("--rename", default=None, action="store_true", dest="rename",
                      help="Rename a variable")
    parser.add_option("--variable", default=None, action="store", dest="variable",
                      help="The name or id of the variable")
    parser.add_option("--ruleset", default=None, action="store", dest="ruleset",
                      help="The name or id of the variable's ruleset")
    parser.add_option("--to", default=None, action="store", dest="to",
                      help="With --rename, set the new variable name")
    parser.add_option("--dest-ruleset", default=None, action="store", dest="dest_ruleset",
                      help="With --copy or --move, set the name or id of the destination ruleset")
    parser.add_option("--class", default=None, action="store", dest="var_class",
                      help="With --set, set the variable class")
    parser.add_option("--value", default=None, action="store", dest="var_value",
                      help="With --set, set the variable value")
    parser.add_option("--value-edit", default=False, action="store_true", dest="var_value_edit",
                      help="With --set, spawn an editor on the variable expected data structure. Upon exit, the edited structure is saved as the variable value.")
    candidates_path = {
      "--ruleset": "/compliance/rulesets",
      "--dest-ruleset": "/compliance/rulesets",
      "--variable": "/compliance/rulesets/<ruleset>/variable",
      "--to": "/compliance/rulesets/<ruleset>/variable",
    }
    api_candidates = False

    def cmd(self, line):
        global path
        try:
            options, args = self.parser.parse_args(args=shlex.split(line))
        except Exception as e:
            try: print(e)
            except: pass
            return
        self.list_variables(options)
        self.add_variable(options)
        self.remove_variable(options)
        self.set_variable(options)
        self.rename_variable(options)
        self.copy_variable(options)
        self.move_variable(options)

    def list_variables(self, options):
        if options.list is None or options.ruleset is None:
            return
        p = "/compliance/rulesets/%s/variables" % options.ruleset
        o = CmdLs(self.cli).cmd(p)

    def add_variable(self, options):
        if options.add is None or options.ruleset is None or options.variable is None:
            return
        global path
        _path = "/compliance/rulesets/%s/variables" % options.ruleset
        data = {
          "var_name": options.variable,
        }
        r = requests.post(self.cli.api+_path, data=data, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def remove_variable(self, options):
        if options.remove is None or options.ruleset is None or options.variable is None:
            return
        global path
        _path = "/compliance/rulesets/%s/variables/%s" % (options.ruleset, options.variable)
        r = requests.delete(self.cli.api+_path, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def rename_variable(self, options):
        if options.rename is None or options.ruleset is None or options.variable is None or options.to is None:
            return
        data = {
          "var_name": options.to,
        }
        _path = "/compliance/rulesets/%s/variables/%s" % (options.ruleset, options.variable)
        r = requests.post(self.cli.api+_path, data=data, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def copy_variable(self, options):
        if options.copy is None or options.ruleset is None or options.variable is None or options.dest_ruleset is None:
            return
        data = {
          "action": "copy",
          "dst_ruleset": options.dest_ruleset,
        }
        _path = "/compliance/rulesets/%s/variables/%s" % (options.ruleset, options.variable)
        r = requests.put(self.cli.api+_path, data=data, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def move_variable(self, options):
        if options.move is None or options.ruleset is None or options.variable is None or options.dest_ruleset is None:
            return
        data = {
          "action": "move",
          "dst_ruleset": options.dest_ruleset,
        }
        _path = "/compliance/rulesets/%s/variables/%s" % (options.ruleset, options.variable)
        r = requests.put(self.cli.api+_path, data=data, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def set_variable(self, options):
        if options.set is None or options.ruleset is None or options.variable is None:
            return
        self.set_variable_class(options)
        self.set_variable_value(options)
        self.set_variable_value_edit(options)

    def set_variable_class(self, options):
        if options.var_class is None:
            return
        data = {
          "var_class": options.var_class,
        }
        _path = "/compliance/rulesets/%s/variables/%s" % (options.ruleset, options.variable)
        r = requests.post(self.cli.api+_path, data=data, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def set_variable_value(self, options):
        if options.var_value is None:
            return
        data = {
          "var_value": options.var_value,
        }
        _path = "/compliance/rulesets/%s/variables/%s" % (options.ruleset, options.variable)
        r = requests.post(self.cli.api+_path, data=data, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def set_variable_value_edit(self, options):
        if not options.var_value_edit:
            return

        # get variable class
        _path = "/compliance/rulesets/%s/variables/%s" % (options.ruleset, options.variable)
        r = requests.get(self.cli.api+_path, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        variable = json.loads(bdecode(r.content))["data"][0]
        variable_class = str(variable["var_class"])
        variable_value = variable["var_value"]

        # get form definition
        _path = "/forms"
        params = {
          "query": "form_name="+variable_class,
        }
        r = requests.get(self.cli.api+_path, params=params, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        form = json.loads(bdecode(r.content))["data"][0]
        form_def = form["form_definition"]

        output_format = form_def["Outputs"][0]["Format"]

        # get current value
        if output_format != "raw":
            try:
                variable_data = json.loads(variable_value)
            except:
                variable_data = None
        else:
            variable_data = variable_value

        if variable_data is not None:
            pass
        elif output_format.endswith("dict"):
            d = {}
            for _input in form_def["Inputs"]:
                if "Key" in _input:
                    k = _input["Key"]
                else:
                    k = _input["Id"]
                v = "<%s. %s>" % (_input.get("Type", ""), _input.get("Help", ""))
                d[k] = v
        else:
            d = ""

        if variable_data is not None:
            text_data = variable_data
        elif output_format == "raw":
            text_data = ""
        elif output_format == "list":
            text_data = [d]
        elif output_format == "list of dict":
            text_data = [d]
        elif output_format == "dict of dict":
            text_data = {"<key>": d}
        elif output_format.startswith("dict"):
            text_data = d
        else:
            print("unknow format")
            return

        import tempfile
        f = tempfile.NamedTemporaryFile(prefix='variable_edit.')
        fname = f.name
        f.close()
        with open(fname, "w") as f:
            f.write(json.dumps(text_data, indent=4))

        os.system(os.environ.get("EDITOR", "vi")+" "+fname)
        with open(fname, "r") as f:
            buff = f.read()
        new_text_data = json.loads(buff)
        os.unlink(fname)
        if new_text_data == text_data:
            print("canceled (no change done in the editor)")
            return

        data = {
          "var_value": buff,
        }
        _path = "/compliance/rulesets/%s/variables/%s" % (options.ruleset, options.variable)
        r = requests.post(self.cli.api+_path, data=data, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)


class CmdRuleset(Cmd):
    command = "ruleset"
    desc = "Handle design actions on a compliance ruleset"
    parser = CmdOptionParser(description=desc)
    parser.add_option("--list", default=None, action="store_true", dest="list",
                      help="List rulesets")
    parser.add_option("--show", default=None, action="store_true", dest="show",
                      help="Show a ruleset design, with nesting")
    parser.add_option("--create", default=None, action="store_true", dest="create",
                      help="Create a ruleset")
    parser.add_option("--delete", default=None, action="store_true", dest="delete",
                      help="Delete a ruleset")
    parser.add_option("--set", default=None, action="store_true", dest="set",
                      help="Set a ruleset property")
    parser.add_option("--rename", default=None, action="store_true", dest="rename",
                      help="Rename a ruleset")
    parser.add_option("--attach", default=None, action="store_true", dest="attach",
                      help="Attach the ruleset to a filterset, a ruleset or a moduleset")
    parser.add_option("--detach", default=None, action="store_true", dest="detach",
                      help="Detach the ruleset from a filterset, a ruleset or a moduleset")
    parser.add_option("--clone", default=None, action="store_true", dest="clone",
                      help="Clone a ruleset, including variables, filterset and ruleset-ruleset relations. Reset the publication as responsible groups.")
    parser.add_option("--ruleset", default=None, action="store", dest="ruleset",
                      help="The name or id of the ruleset to manage")
    parser.add_option("--filterset", default=None, action="store", dest="filterset",
                      help="The name or id of the filterset to attach or detach")
    parser.add_option("--parent-ruleset", default=None, action="store", dest="parent_ruleset",
                      help="The name or id of the ruleset to attach to or detach from")
    parser.add_option("--parent-moduleset", default=None, action="store", dest="parent_moduleset",
                      help="The name or id of the moduleset to attach to or detach from")
    parser.add_option("--publication-group", default=None, action="store", dest="publication_group",
                      help="The name or id of the group to attach or detach as publication")
    parser.add_option("--responsible-group", default=None, action="store", dest="responsible_group",
                      help="The name or id of the group to attach or detach as responsible")
    parser.add_option("--public", default=None, action="store_true", dest="public",
                      help="With --set, set the public property to true")
    parser.add_option("--not-public", default=None, action="store_false", dest="public",
                      help="With --set, set the public property to false")
    parser.add_option("--contextual", default=None, action="store_true", dest="contextual",
                      help="With --set, set the type property to contextual")
    parser.add_option("--explicit", default=None, action="store_false", dest="explicit",
                      help="With --set, set the type property to explicit")
    parser.add_option("--to", default=None, action="store", dest="to",
                      help="with --rename, set the new ruleset name")
    candidates_path = {
      "--ruleset": "/compliance/rulesets",
      "--parent-moduleset": "/compliance/modulesets",
      "--parent-ruleset": "/compliance/rulesets",
      "--publication-group": "/groups",
      "--responsible-group": "/groups",
      "--filterset": "/filtersets",
    }
    api_candidates = False

    def cmd(self, line):
        global path
        try:
            options, args = self.parser.parse_args(args=shlex.split(line))
        except Exception as e:
            try: print(e)
            except: pass
            return
        self.clone_ruleset(options)
        self.list_rulesets(options)
        self.show_ruleset(options)
        self.create_ruleset(options)
        self.delete_ruleset(options)
        self.set_ruleset(options)
        self.rename_ruleset(options)
        self.attach_filterset_to_ruleset(options)
        self.detach_filterset_from_ruleset(options)
        self.attach_publication_group_to_ruleset(options)
        self.detach_publication_group_from_ruleset(options)
        self.attach_responsible_group_to_ruleset(options)
        self.detach_responsible_group_from_ruleset(options)
        self.attach_ruleset_to_ruleset(options)
        self.attach_ruleset_to_moduleset(options)
        self.detach_ruleset_from_ruleset(options)
        self.detach_ruleset_from_moduleset(options)

    def list_rulesets(self, options):
        if options.list is None:
            return
        p = "/compliance/rulesets"
        o = CmdLs(self.cli).cmd(p)

    def show_ruleset(self, options):
        if options.show is None:
            return
        o = CmdShow(self.cli)
        data = o.get_data("/compliance/rulesets/"+options.ruleset)
        o.print_ruleset(options.ruleset, data)

    def clone_ruleset(self, options):
        if options.clone is None or options.ruleset is None:
            return
        data = {
          "action": "clone",
        }
        _path = "/compliance/rulesets/%s" % options.ruleset
        r = requests.put(self.cli.api+_path, data=data, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def create_ruleset(self, options):
        if options.create is None or options.ruleset is None:
            return
        global path
        _path = "/compliance/rulesets"
        data = {
          "ruleset_name": options.ruleset,
        }
        r = requests.post(self.cli.api+_path, data=data, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def delete_ruleset(self, options):
        if options.delete is None or options.ruleset is None:
            return
        global path
        _path = "/compliance/rulesets/"+options.ruleset
        r = requests.delete(self.cli.api+_path, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def set_ruleset(self, options):
        if options.set is None or options.ruleset is None:
            return
        self.set_ruleset_public(options)
        self.set_ruleset_type(options)

    def set_ruleset_public(self, options):
        if options.public is None:
            return
        data = {
          "ruleset_public": options.public,
        }
        _path = "/compliance/rulesets/%s" % options.ruleset
        r = requests.post(self.cli.api+_path, data=data, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def set_ruleset_type(self, options):
        if options.contextual is None and options.explicit is None:
            return
        if options.contextual is not None and options.explicit is not None:
            print("don't set both --explicit and --contextual")
            return
        if options.contextual:
            t = "contextual"
        if options.explicit:
            t = "explicit"
        data = {
          "ruleset_type": t,
        }
        _path = "/compliance/rulesets/%s" % options.ruleset
        r = requests.post(self.cli.api+_path, data=data, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def attach_publication_group_to_ruleset(self, options):
        if options.attach is None or options.publication_group is None or options.ruleset is None:
            return
        _path = "/compliance/rulesets/%s/publications/%s" % (options.ruleset, options.publication_group)
        r = requests.post(self.cli.api+_path, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def detach_publication_group_from_ruleset(self, options):
        if options.detach is None or options.publication_group is None or options.ruleset is None:
            return
        _path = "/compliance/rulesets/%s/publications/%s" % (options.ruleset, options.publication_group)
        r = requests.delete(self.cli.api+_path, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def attach_responsible_group_to_ruleset(self, options):
        if options.attach is None or options.responsible_group is None or options.ruleset is None:
            return
        _path = "/compliance/rulesets/%s/responsibles/%s" % (options.ruleset, options.responsible_group)
        r = requests.post(self.cli.api+_path, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def detach_responsible_group_from_ruleset(self, options):
        if options.detach is None or options.responsible_group is None or options.ruleset is None:
            return
        _path = "/compliance/rulesets/%s/responsibles/%s" % (options.ruleset, options.responsible_group)
        r = requests.delete(self.cli.api+_path, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def attach_filterset_to_ruleset(self, options):
        if options.attach is None or options.filterset is None or options.ruleset is None:
            return
        _path = "/compliance/rulesets/%s/filtersets/%s" % (options.ruleset, options.filterset)
        r = requests.post(self.cli.api+_path, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def detach_filterset_from_ruleset(self, options):
        if options.detach is None or options.filterset is None or options.ruleset is None:
            return
        _path = "/compliance/rulesets/%s/filtersets/%s" % (options.ruleset, options.filterset)
        r = requests.delete(self.cli.api+_path, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def attach_ruleset_to_ruleset(self, options):
        if options.attach is None or options.parent_ruleset is None or options.ruleset is None:
            return
        _path = "/compliance/rulesets/%s/rulesets/%s" % (options.parent_ruleset, options.ruleset)
        r = requests.post(self.cli.api+_path, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def attach_ruleset_to_moduleset(self, options):
        if options.attach is None or options.parent_moduleset is None or options.ruleset is None:
            return
        _path = "/compliance/modulesets/%s/rulesets/%s" % (options.parent_moduleset, options.ruleset)
        r = requests.post(self.cli.api+_path, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def detach_ruleset_from_ruleset(self, options):
        if options.detach is None or options.parent_ruleset is None or options.ruleset is None:
            return
        _path = "/compliance/rulesets/%s/rulesets/%s" % (options.parent_ruleset, options.ruleset)
        r = requests.delete(self.cli.api+_path, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def detach_ruleset_from_moduleset(self, options):
        if options.detach is None or options.parent_moduleset is None or options.ruleset is None:
            return
        _path = "/compliance/modulesets/%s/rulesets/%s" % (options.parent_moduleset, options.ruleset)
        r = requests.delete(self.cli.api+_path, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)

    def rename_ruleset(self, options):
        if options.rename is None or options.ruleset is None or options.to is None:
            return
        data = {
          "ruleset_name": options.to,
        }
        _path = "/compliance/rulesets/%s" % options.ruleset
        r = requests.post(self.cli.api+_path, data=data, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        self.print_content(r.content)


class CmdShow(Cmd):
    api_candidates = True
    command = "show"
    desc = "Show a moduleset or a ruleset design and nesting."
    parser = CmdOptionParser(description=desc)

    def label(self, s):
        s = s.rstrip(":") + ":"
        if s in ("ruleset:", "type:", "public:", "filterset:", "stats:"):
            return self.colorize(s, c=self.GREEN)
        elif s in ("moduleset:"):
            return  self.colorize(s, c=self.DARKCYAN)
        elif s in ("publication group:", "responsible group:"):
            return  self.colorize(s, c=self.BLUE)
        elif s in ("variable:", "module:"):
            return  self.colorize(s, c=self.RED)
        return s

    def get_data(self, _path):
        r = requests.get(self.cli.api+_path+"/export", auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        data = json.loads(bdecode(r.content))

        # load hashes
        self.rulesets = {}
        for e in data.get("rulesets", []):
            self.rulesets[e.get("ruleset_name")] = e
        self.modulesets = {}
        for e in data.get("modulesets", []):
            self.modulesets[e.get("modset_name")] = e
        self.filtersets = {}
        for e in data.get("filtersets", []):
            self.modulesets[e.get("fset_name")] = e

        return data

    def cmd(self, line):
        global path
        try:
            options, args = self.parser.parse_args(args=shlex.split(line))
        except Exception as e:
            try: print(e)
            except: pass
            return
        _path = self.args_to_path(args)
        data = self.get_data(_path)
        _path = self.factorize_dot_dot(_path)
        obj_type = _path.split("/")[-2]
        obj_id = _path.split("/")[-1]
        try:
            obj_id = int(obj_id)
        except:
            pass

        if obj_type == "modulesets":
            self.print_moduleset(obj_id, data)
        elif obj_type == "rulesets":
            self.print_ruleset(obj_id, data)
        elif obj_type == "filtersets":
            self.print_filterset(obj_id, data)
        else:
            print("unsupported object type:", obj_type)

    def print_moduleset(self, obj_id, data):
        for e in data.get("modulesets", []):
            if e.get("modset_name") == obj_id or e.get("id") == obj_id:
                self.print_export_moduleset(e, data)

    def print_ruleset(self, obj_id, data):
        for e in data.get("rulesets", []):
            if e.get("ruleset_name") == obj_id or e.get("id") == obj_id:
                self.print_export_ruleset(e, data)

    def print_filterset(self, obj_id, data):
        for e in data.get("filtersets", []):
            if e.get("fset_name") == obj_id or e.get("id") == obj_id:
                self.print_export_filterset(e, data)

    def iprint(self, *args, **vars):
        lvl = vars.get("lvl", 0)
        if lvl > 0:
            args = ["    "*lvl] + list(args)
        print(*args)

    def print_export_rulesets(self, data, lvl=0):
        for e in data["rulesets"]:
            self.print_export_ruleset(e, data, lvl=lvl)

    def print_export_modulesets(self, data, lvl=0):
        for e in data["modulesets"]:
            self.print_export_moduleset(e, data, lvl=lvl)

    def print_export_filtersets(self, data, lvl=0):
        for e in data["filtersets"]:
            self.print_export_filterset(e, data, lvl=lvl)

    def print_export_ruleset(self, rset, data, lvl=0):
        self.iprint(self.label("ruleset"), rset.get("ruleset_name"), lvl=lvl)
        self.iprint(self.label("public"), rset.get("ruleset_public"), lvl=lvl+1)
        self.iprint(self.label("type"), rset.get("ruleset_type"), lvl=lvl+1)
        if rset.get("fset_name"):
            self.iprint(self.label("filterset"), rset.get("fset_name"), lvl=lvl+1)
        for e in rset.get("publications"):
            self.iprint(self.label("publication group"), e, lvl=lvl+1)
        for e in rset.get("responsibles"):
            self.iprint(self.label("responsible group"), e, lvl=lvl+1)
        for e in rset.get("variables"):
            self.iprint(self.label("variable"), e.get("var_class"), e.get("var_name"), lvl=lvl+1)
        for e in rset.get("rulesets"):
            _e = self.rulesets.get(e)
            if _e is None:
                continue
            self.print_export_ruleset(_e, data, lvl=lvl+1)

    def print_export_moduleset(self, modset, data, lvl=0):
        self.iprint(self.label("moduleset"), modset.get("modset_name"), lvl=lvl)
        for e in modset.get("publications"):
            self.iprint(self.label("publication group"), e, lvl=lvl+1)
        for e in modset.get("responsibles"):
            self.iprint(self.label("responsible group"), e, lvl=lvl+1)
        for e in modset.get("modules"):
            autofix = e.get("autofix")
            if autofix:
                autofix = "(autofix)"
            else:
                autofix = ""
            self.iprint(self.label("module"), e.get("modset_mod_name"), autofix, lvl=lvl+1)
        for e in modset.get("rulesets"):
            _e = self.rulesets.get(e)
            if _e is None:
                continue
            self.print_export_ruleset(_e, data, lvl=lvl+1)
        for e in modset.get("modulesets"):
            _e = self.modulesets.get(e)
            if _e is None:
                continue
            self.print_export_moduleset(_e, data, lvl=lvl+1)

    def print_export_filterset(self, rset, data, lvl=0):
        self.iprint(self.label("filterset"), rset.get("fset_name"), lvl=lvl)
        self.iprint(self.label("stats"), rset.get("fset_stats"), lvl=lvl+1)
        for e in rset.get("filters"):
            if e.get("filterset"):
                self.iprint(self.colorize(str(e.get("f_order"))+":", c=self.RED),
                            e.get("f_log_op"),
                            e.get("filterset"),
                            lvl=lvl+1)
            else:
                f = e.get("filter")
                self.iprint(self.colorize(str(e.get("f_order"))+":", c=self.RED),
                            e.get("f_log_op"),
                            f.get("f_table")+"."+f.get("f_field"), f.get("f_op"), f.get("f_value"),
                            lvl=lvl+1)


def validate_response(r):
    if r.status_code == 200:
        return
    try:
        data = json.loads(r.content)
        raise CliError("%d %s" % (r.status_code, data["error"]))
    except (ValueError, KeyError):
        pass
    raise CliError(str(r))

class CmdGet(Cmd):
    api_candidates = True
    command = "get"
    desc = "Execute a GET request on the given API handler. The parameters can be set using --<param>."
    parser = CmdOptionParser(description=desc)

    def cmd(self, line):
        self.set_parser_options_from_cmdline(line)
        global path
        try:
            options, args = self.parser.parse_args(args=shlex.split(line))
        except Exception as e:
            try: print(e)
            except: pass
            return
        params = options.__dict__
        _path = self.args_to_path(args)
        r = requests.get(self.cli.api+_path, params=params, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        try:
            # try not to display \u0000 in the output
            d = json.loads(bdecode(r.content))
            self.print_content(json.dumps(d, ensure_ascii=False, indent=8))
        except Exception as e:
            self.print_content(r.content)

class CmdHistory(Cmd):
    command = "history"
    desc = "Display the commands history"
    parser = CmdOptionParser(description=desc)
    max_lines = 200

    def candidates(self, p):
        return []

    def cmd(self, line):
        n = readline.get_current_history_length()
        if n > self.max_lines:
            m = self.max_lines
        else:
            m = n
        print("n", n)
        print("m", m)
        for i in range(n-m, n):
            print("%-6d %s" % (i, readline.get_history_item(i)))

class CmdCd(Cmd):
    api_candidates = True
    command = "cd"
    desc = "Change the current working directory in the API handlers tree."
    parser = CmdOptionParser(description=desc)
    prev_paths = ["/"]
    max_prev_paths = 10

    def append_to_prev_paths(self, p):
        global path
        if path == self.prev_paths[-1]:
            return
        self.prev_paths.append(copy.copy(path))
        if len(self.prev_paths) <= self.max_prev_paths:
            return
        for i in range(len(self.prev_paths)-self.max_prev_paths):
            dump = self.prev_paths.pop(0)

    def set_new_path(self, p):
        global path
        self.append_to_prev_paths(p)
        path = p

    def cmd(self, line):
        global path
        m = re.match(r"^cd\s+(?P<path>[% @\-\./\w]+)$", line)
        if m is None:
            return
        p = m.group("path")

        # handle "cd -"
        if p == "-":
            new_path = self.prev_paths.pop()
            self.set_new_path(new_path)
            return

        if p != "/":
            p = p.rstrip("/")

        l = path.split("/")
        v = p.split("/")
        for elem in copy.copy(v):
            if elem != "..":
                break
            l.pop()
            v.pop(0)
        new_path = "/".join(l)
        if new_path == "":
            new_path = "/"
        p = "/".join(v)
        if p == "":
            self.set_new_path(new_path)
            return

        if p.startswith("/"):
            new_path = p
        else:
            new_path += "/" + p
        new_path = new_path.replace("//", "/")
        if self.path_match_handlers_or_parents(new_path):
            self.set_new_path(new_path)
            return
        print("path not found")
        return

def path_match_handler(p, d):
    if p == "/":
        return True
    p = p.rstrip("/")
    pattern = d["pattern"]
    if re.match(pattern, p) is not None:
        return True
    return False

def path_match_handler_or_parents(p, d):
    if p == "/":
        return True
    pattern = d["pattern"]
    if re.match(pattern, p) is not None:
        return True
    for i in range(pattern.count("/")):
        pattern = pattern[:pattern.rindex("/")]+"[/]*$"
        if re.match(pattern, p) is not None:
            return True
    return False

class Completer(object):

    def __init__(self, commands):
        self.commands = commands
        self.current_candidates = []
        self.commands_h = {}
        for c in commands:
            self.commands_h[c.command] = c
        self.base_commands = self.commands_h.keys()

    def complete(self, text, state):
        response = None
        if state == 0:
            # This is the first time for this text, so build a match list.

            origline = readline.get_line_buffer()
            begin = readline.get_begidx()
            end = readline.get_endidx()
            being_completed = origline[begin:end]
            words = origline.split()

            #print('origline=%s'% repr(origline))
            #print('begin=%s'% begin)
            #print('end=%s'% end)
            #print('being_completed=%s'% being_completed)
            #print('words=%s'% words)

            self.current_candidates = sorted(self.base_commands)
            try:
                if begin == 0:
                    # first word
                    candidates = self.current_candidates
                else:
                    # later word
                    command = " ".join(words)
                    while (command != ""):
                        if command in self.commands_h:
                            c = self.commands_h[command]
                            break
                        command = " ".join(command.split()[:-1])
                    if command == "":
                        raise KeyError("command not supported")
                    candidates = []
                    candidates += c.candidates(being_completed, words)
                if being_completed:
                    # match options with portion of input
                    # being completed
                    self.current_candidates = [ w for w in candidates
                                                if w.startswith(being_completed) ]
                else:
                    # matching empty string so use all candidates
                    self.current_candidates = candidates

                #print('candidates=%s', self.current_candidates)

            except (KeyError, IndexError) as err:
                self.current_candidates = []

        try:
            response = self.current_candidates[state]
        except IndexError:
            response = None
        #print('complete(%s, %s) => %s'% (repr(text), state, response))
        return response


class Cli(object):
    def __init__(self, user=None, password=None, api=None, insecure=None, refresh_api=None, fmt=None, config=conf_f, save=True):
        self.options = Storage({
            "user": user,
            "password": password,
            "api": api,
            "refresh_api": refresh_api,
            "insecure": insecure,
            "config": config,
            "format": fmt,
            "save": save,
        })

        self.read_config()
        self.parse_options()
        self.do_refresh_api()

        self.commands = [
          CmdCd(cli=self),
          CmdLs(cli=self),
          CmdHistory(cli=self),
          CmdGet(cli=self),
          CmdPost(cli=self),
          CmdPut(cli=self),
          CmdDelete(cli=self),
          CmdShow(cli=self),
          CmdHelp(cli=self),
          CmdRuleset(cli=self),
          CmdVariable(cli=self),
          CmdModuleset(cli=self),
          CmdModule(cli=self),
          CmdFilter(cli=self),
          CmdFilterset(cli=self),
          CmdSysreport(cli=self),
          CmdSafe(cli=self),
        ]


    def dispatch(self, line):
        if line.strip() == "":
            return
        for command in self.commands:
            if not command.match(line):
                continue
            try:
                return command.cmd(line)
            except CliError as e:
                print(str(e), file=sys.stderr)
                return 1
        print("command not found:", line)

    def parse_options(self):
        self.need_save = False
        self.user = self.set_option("user")
        self.password = self.set_option("password")
        self.api = self.set_option("api")
        self.format = self.set_option("format", "json")
        self.insecure = self.set_option("insecure", False)
        self.auth = (self.user, self.password)

        if self.insecure and InsecureRequestWarning is not None:
            requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

        self.host = self.api.replace("https://", "").replace("http://", "")
        if "/" in self.host:
            self.host = self.host[:self.host.index("/")]

        if not self.api.endswith("/rest/api"):
            self.api = "https://" + self.host + "/init/rest/api"

        if self.need_save and self.options.save:
            self.save_config()

    def save_config(self):
        """ Save options if no config file is present yet.
        """
        if self.user is None or self.password is None or self.api is None or self.insecure is None:
            return
        print("updating %s config file with provided parameters" % self.options.config, file=sys.stderr)
        if not self.conf.has_section(conf_section):
            self.conf.add_section(conf_section)
        self.conf.set(conf_section, "user", self.user)
        self.conf.set(conf_section, "password", self.password)
        self.conf.set(conf_section, "api", self.api)
        self.conf.set(conf_section, "insecure", self.insecure)
        self.conf.set(conf_section, "format", self.format)
        with open(self.options.config, 'w') as fp:
            self.conf.write(fp)
        os.chmod(self.options.config, 0o0600)

    def do_refresh_api(self):
        try:
            self.api_o = Api(cli=self, refresh=self.options.refresh_api)
        except Exception as e:
            raise ex.excError(str(e))

    def read_config(self):
        if os.path.exists(self.options.config):
            s = os.stat(self.options.config)
            if s.st_mode & stat.S_IWOTH:
                print("set ", self.options.config, "mode to 0600")
                os.chmod(self.options.config, 0o0600)

        try:
            self.conf = ConfigParser.RawConfigParser()
            self.conf.read(self.options.config)
        except:
            pass

    def set_option(self, o, default=None):
        if self.options[o] == "?":
            self.need_save = True
            if o == "password":
                import getpass
                return getpass.getpass()
            else:
                return raw_input(o+": ")
        if self.options[o] is not None:
            self.need_save = True
            return self.options[o]
        if self.conf.has_option(conf_section, o):
            return self.conf.get(conf_section, o)
        if default is not None:
            return default
        raise ex.excError("missing parameter: "+o)

    def dispatch_noninteractive(self, args):
        # non interactive mode
        import subprocess
        line = subprocess.list2cmdline(args)
        try:
            return self.dispatch(line)
        except Exception as exc:
            raise ex.excError(str(exc))

    def readline_setup(self):
        readline.parse_and_bind('tab: complete')
        atexit.register(readline.write_history_file, history_f)
        try:
            readline.read_history_file(history_f)
        except IOError:
            pass
        readline.set_completer(Completer(self.commands).complete)
        delims = readline.get_completer_delims()
        delims = delims.replace("-", "").replace("/", "").replace("@", "").replace("%", "")
        readline.set_completer_delims(delims)

    def input_loop(self):
        self.readline_setup()
        line = ''
        while line not in ('exit', 'quit'):
            try:
                line = raw_input(self.host+":"+path+' # ')
                self.dispatch(line)
            except ValueError as exc:
                print(exc)
                readline.redisplay()
                pass
            except EOFError:
                print()
                return
            except KeyboardInterrupt:
                print()
                readline.redisplay()
                pass
            except Exception as e:
                import traceback
                e = sys.exc_info()
                print(e[0], e[1], traceback.print_tb(e[2]))


    def run(self, argv=None):
        if argv and len(argv) > 0:
            return self.dispatch_noninteractive(argv)
        else:
            self.input_loop()

if __name__ == "__main__":
    cli = Cli()
    ret = cli.run()
    sys.exit(ret)

 0707010005c91f000081b40000000000000000000000015ab2175400002573000000660000000000000000000000000000002600000000root/usr/share/opensvc/lib/rcColor.py from __future__ import print_function
import os
import sys
import platform
import rcExceptions as ex
from rcUtilities import is_string

use_color = "auto"

unicons = {
    "frozen": "*",
    "stdby up": "o",
    "stdby down": "x",
    "up": "O",
    "down": "X",
    "warn": "!",
    "n/a": "/",
    "undef": "/",
}

class color:
    END = '\033[000m'
    BOLD = '\033[001m'
    UNDERLINE = '\033[004m'

    BLACK = '\033[030m'
    RED = '\033[031m'
    GREEN = '\033[032m'
    BROWN = '\033[033m'
    BLUE = '\033[034m'
    PURPLE = '\033[035m'
    CYAN = '\033[036m'
    GRAY = '\033[037m'

    DARKGRAY = '\033[090m'
    LIGHTRED = '\033[091m'
    LIGHTGREEN = '\033[092m'
    YELLOW = '\033[093m'
    LIGHTBLUE = '\033[094m'
    LIGHTPURPLE = '\033[095m'
    LIGHTCYAN = '\033[096m'
    WHITE = '\033[097m'

    BGBLACK = '\033[040m'
    BGRED = '\033[041m'
    BGGREEN = '\033[042m'
    BGYELLOW = '\033[043m'
    BGBLUE = '\033[044m'
    BGPURPLE = '\033[045m'
    BGCYAN = '\033[046m'
    BGWHITE = '\033[047m'
    BGDEFAULT = '\033[049m'
    BGGRAY = '\033[100m'

    E_BGODD = '\033[48;2;240;240;205m'
    E_BGCYAN = '\033[48;2;125;205;205m'

if os.environ.get("TERM") in ("screen-256color", "xterm-256color", "screen-256color-bce"):
    color.LIGHTBLUE = '\033[038;5;243m'

STATUS_COLOR = {
    "up": color.GREEN,
    "stdby up": color.GREEN,
    "ok": color.GREEN,
    "down": color.RED,
    "stdby down": color.RED,
    "err": color.RED,
    "error": color.RED,
    "n/a": color.LIGHTBLUE,
    "undef": color.LIGHTBLUE,
    "unknown": color.LIGHTBLUE,
    "warn": color.BROWN,
}

def ansi_colorize(s, c=None):
    global use_color
    if c is None:
        return s
    if use_color in ("never", "no") or (use_color == "auto" and not os.isatty(1)):
        return s
    return c + s + color.END

def win_colorize(s, c=None):
    return s

if platform.system() == 'Windows':
    colorize = win_colorize
else:
    colorize = ansi_colorize

def colorize_json(s):
    import re
    from rcStatus import colorize_status
    s = re.sub(r'(")(error|ok|err|up|down|warn|n/a|stdby up|stdby down)(")', lambda m: m.group(1)+colorize_status(m.group(2), lpad=0)+m.group(3), s)
    s = re.sub(r'((?!"DEFAULT":)("[\w: ,@-]+":))', colorize(r'\1', color.LIGHTBLUE), s)
    s = re.sub(r'("DEFAULT":)( {)', colorize(r'\1', color.BROWN)+r'\2', s)
    s = re.sub(r'("[\w:-]+#[\w\.:-]+":)( {)', colorize(r'\1', color.BROWN)+r'\2', s)
    s = re.sub(r'(@[\w-]+)(":)', colorize(r'\1', color.RED)+colorize(r'\2', color.LIGHTBLUE), s)
    s = re.sub(r'({.+})', colorize(r'\1', color.GREEN), s)
    return s

def format_json(d):
    import json

    kwargs = {
      "sort_keys": True,
      "ensure_ascii": False,
      "indent": 4,
      "separators": (',', ': '),
    }
    if sys.version_info[0] < 3:
        kwargs["encoding"] = "utf8"
    colorize_json(json.dumps(d, **kwargs))
    try:
        print(colorize_json(json.dumps(d, **kwargs)))
    except IOError:
        pass
    except:
        kwargs["ensure_ascii"] = True
        print(colorize_json(json.dumps(d, **kwargs)))

def format_table(d):
    from rcPrintTable import print_table_tabulate
    print_table_tabulate(d)

def format_default(d):
    from rcPrintTable import print_table_default
    if "error" in d and is_string(d["error"]):
        print(d["error"], file=sys.stderr)
    print_table_default(d)

def format_csv(d):
    from rcPrintTable import print_table_csv
    print_table_csv(d)

def is_list_of_list(d):
    if type(d) != list:
        return False
    if len(d) == 2 and type(d[0]) == list and type(d[1]) == list:
        return True
    if len(d) > 0 and type(d[0]) == list:
        return True
    return False

def is_list_of_dict(d):
    if type(d) != list:
        return False
    if len(d) == 0:
        return False
    for e in d:
        if type(e) != dict:
            return False
    return True

def is_dict_of_list(d):
    if type(d) != dict:
        return False
    for k, v in d.items():
        if not is_list_of_list(v):
            return False
    return True

def is_dict_of_list_of_dict(d):
    if type(d) != dict:
        return False
    for k, v in d.items():
        if not is_list_of_dict(v):
            return False
    return True

def is_dict_of_list_of_list(d):
    if type(d) != dict:
        return False
    for k, v in d.items():
        if not is_list_of_list(v):
            return False
    return True

def flatten_list(data):
    for idx, entry in enumerate(data):
        if not isinstance(entry, dict):
            continue
        for key, val in entry.items():
            if not isinstance(val, dict):
                continue
            for _key, _val in val.items():
                agg_key = key + "." + _key
                data[idx][agg_key] = _val
            del data[idx][key]
    return data

def xform_data_for_tabular(d):
    if is_list_of_dict(d):
        return _xform_ld_data_for_tabular(d)
    if is_dict_of_list_of_dict(d):
        return _xform_dld_data_for_tabular(d)
    if is_dict_of_list_of_list(d):
        return _xform_dll_data_for_tabular(d)
    return d

def _xform_dll_data_for_tabular(d):
    l = []
    for k, v in d.items():
        if len(v) == 0:
            continue
        v[0].insert(0, "service")
        for i, e in enumerate(v[1:]):
            v[i+1].insert(0, k)
        if len(l) == 0:
            l += v
        else:
            l += v[1:]
    return l

def _xform_dld_data_for_tabular(d):
    l = []
    for k, v in d.items():
        if len(l) == 0:
            l += _xform_ld_data_for_tabular(v, include_header=True, prepend=("service", k))
        else:
            l += _xform_ld_data_for_tabular(v, include_header=False, prepend=("service", k))
    return l

def _xform_ld_data_for_tabular(d, include_header=True, prepend=None):
    d = flatten_list(d)
    l = []
    if include_header:
        header = list(d[0].keys())
        if prepend:
            header.insert(0, prepend[0])
        l += [header]
    for e in d:
        values = list(e.values())
        if prepend:
            values.insert(0, prepend[1])
        l.append(values)
    return l
    
def xform_data_for_json(d):
    if is_list_of_list(d):
        return _xform_data_for_json(d)
    if is_dict_of_list(d):
        for k in d:
            d[k] = _xform_data_for_json(d[k])
    return d

def _xform_data_for_json(d):
    if len(d) < 2:
        return []
    l = []
    titles = d[0]
    for _d in d[1:]:
        h = {}
        for a, b in zip(titles, _d):
            h[a] = b
        l.append(h)
    return l

def formatter(fn):
    def decorator(*args, **kwargs):
        fmt = args[0].options.format

        if fmt == "json":
            _fmt = format_json
        elif fmt == "table":
            _fmt = format_table
        elif fmt == "csv":
            _fmt = format_csv
        elif fmt is None or fmt == "default":
            _fmt = format_default
        elif hasattr(fmt, "__call__"):
            _fmt = fmt
        else:
            raise ex.excError("unsupported output format: %s" % str(fmt))

        data = fn(*args, **kwargs)

        if fmt == "json":
            data = xform_data_for_json(data)
        elif fmt in ("table", "csv", "default", None):
            data = xform_data_for_tabular(data)

        if data is None:
            return
        if type(data) in (int, float):
            return
        if fmt != "json" and len(data) == 0:
            return

        if not isinstance(data, (dict, list)):
            print(data)
            return

        try:
            _fmt(data)
        except IOError as exc:
            if exc.errno == 32:
                # broken pipe (ex: tail, pager, ...)
                pass
            else:
                raise

        if "error" in data:
            return 1

    return decorator

def print_color_config(fpath):
    """
    Colorize and print the content of the file passed as argument.
    """
    from rcColor import colorize, color
    import re
    def highlighter(line):
        """
        Colorize interesting parts to help readability
        """
        line = line.rstrip("\n")
        if re.match(r'\[.+\]', line):
            return colorize(line, color.BROWN)
        line = re.sub(
            r"({[\.\w\-_#{}\[\]()\$\+]+})",
            colorize(r"\1", color.GREEN),
            line
        )
        line = re.sub(
            r"^(\s*\w+\s*)=",
            colorize(r"\1", color.LIGHTBLUE)+"=",
            line
        )
        line = re.sub(
            r"^(\s*\w+)(@[\w\.]+\s*)=",
            colorize(r"\1", color.LIGHTBLUE)+colorize(r"\2", color.RED)+"=",
            line
        )
        return line
    try:
        with open(fpath, 'r') as ofile:
            for line in ofile.readlines():
                print(highlighter(line))
    except Exception as exc:
        raise ex.excError(exc)

def colorize_log_line(line, last=None):
    """
    Format a log line, colorizing the log level.
    Return the line as a string buffer.
    """
    import rcLogger
    line = line.rstrip("\n")
    elements = line.split(" - ")

    if len(elements) < 3 or elements[2] not in ("DEBUG", "INFO", "WARNING", "ERROR"):
        return

    elements[1] = rcLogger.namefmt % elements[1]
    elements[1] = colorize(elements[1], color.BOLD)
    elements[2] = "%-7s" % elements[2]
    elements[2] = elements[2].replace("ERROR", colorize("ERROR", color.RED))
    elements[2] = elements[2].replace("WARNING", colorize("WARNING", color.BROWN))
    elements[2] = elements[2].replace("INFO", colorize("INFO", color.LIGHTBLUE))
    return " ".join(elements)


 0707010005c920000081b40000000000000000000000015ab217540000081b000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/rcConfigParser.py  from __future__ import print_function
import sys

try:
    import ConfigParser
except ImportError:
    import configparser as ConfigParser

ParsingError = ConfigParser.ParsingError
NoOptionError = ConfigParser.NoOptionError

if sys.version_info[0] < 3:
    class RawConfigParser(ConfigParser.RawConfigParser):
        def __init__(self, *args, **kwargs):
            ConfigParser.RawConfigParser.__init__(self, *args, **kwargs)

        def write(self, fp):
            """
            Write an .ini formatted representation of the configuration.
            """
            encoding = sys.stdin.encoding if sys.stdin.encoding else 'UTF-8'
            if self._defaults:
                fp.write("[%s]\n" % ConfigParser.DEFAULTSECT)
                for (key, value) in self._defaults.items():
                    if not isinstance(value, (str, unicode)):
                        value = str(value)
                    if not isinstance(value, unicode):
                        value = value.decode(encoding)
                    fp.write("%s = %s\n" % (key, value.replace('\n', '\n\t')))
                fp.write("\n")
            for section in self._sections:
                fp.write("[%s]\n" % section)
                for (key, value) in self._sections[section].items():
                    if key == "__name__":
                        continue
                    if not isinstance(value, (str, unicode)):
                        value = str(value)
                    if not isinstance(value, unicode):
                        value = value.decode(encoding)
                    if (value is not None) or (self._optcre == self.OPTCRE):
                        key = " = ".join((key, value.replace('\n', '\n\t')))

                    fp.write("%s\n" % (key.encode("utf-8")))
                fp.write("\n")
else:
    class RawConfigParser(ConfigParser.RawConfigParser):
        def __init__(self, *args, **kwargs):
            if sys.version_info[0] >= 3:
                kwargs["strict"] = False
            ConfigParser.RawConfigParser.__init__(self, *args, **kwargs)
 0707010005c921000081b40000000000000000000000015ab2175400002511000000660000000000000000000000000000002400000000root/usr/share/opensvc/lib/rcDcs.py   from __future__ import print_function

import os
import uuid
from xml.etree.ElementTree import XML, fromstring

try:
    import ConfigParser
except ImportError:
    import configparser as ConfigParser

import rcExceptions as ex
from rcGlobalEnv import rcEnv
from rcUtilities import justcall, which

if rcEnv.paths.pathbin not in os.environ['PATH']:
    os.environ['PATH'] += ":"+rcEnv.paths.pathbin

def dcscmd(cmd, manager, username, password, dcs=None, conn=None):
    if conn is None:
        conn = uuid.uuid1().hex

    if len(cmd) == 0:
        return

    _cmd = ['ssh', manager]
    if dcs is not None:
        _cmd += ["connect-dcsserver -server %s -username %s -password %s -connection %s ; "%(dcs, username, password, conn)+\
                 cmd+ " ; disconnect-dcsserver -connection %s"%conn]
    else:
        _cmd += [cmd]
    out, err, ret = justcall(_cmd)
    if "ErrorId" in err:
        print(_cmd)
        print(out)
        raise ex.excError("dcs command execution error")
    try:
        out = out.decode("latin1").encode("utf8")
    except:
        pass
    return out, err, ret

class Dcss(object):
    arrays = []

    def __init__(self, objects=[]):
        self.objects = objects
        if len(objects) > 0:
            self.filtering = True
        else:
            self.filtering = False
        cf = rcEnv.paths.authconf
        if not os.path.exists(cf):
            return
        conf = ConfigParser.RawConfigParser()
        conf.read(cf)
        m = []
        for s in conf.sections():
            try:
                stype = conf.get(s, 'type')
            except:
                continue
            if stype != "datacore":
                continue
            try:
                manager = s
                dcs = conf.get(s, 'dcs').split()
                username = conf.get(s, 'username')
                password = conf.get(s, 'password')
                m += [(manager, dcs, username, password)]
            except:
                print("error parsing section", s)
                pass
        del(conf)
        done = []
        for manager, dcs, username, password in m:
            for name in dcs:
                if self.filtering and name not in self.objects:
                    continue
                if name in done:
                    continue
                self.arrays.append(Dcs(name, manager, username, password))
                done.append(name)

    def __iter__(self):
        for array in self.arrays:
            yield(array)

    def get_dcs(self, domain):
        for dcs in self.arrays:
            _domain = dcs.get_domain()
            if _domain == domain:
                return dcs
        return None

class Dcs(object):
    def __init__(self, name, manager, username, password, conn=None):
        self.name = name
        self.manager = manager
        self.username = username
        self.password = password
        self.conn = conn
        if conn is None:
            self.conn = uuid.uuid1().hex
        self.keys = ['dcsservergroup',
                     'dcsserver',
                     'dcspool',
                     'dcspoolperf',
                     'dcslogicaldisk',
                     'dcslogicaldiskperf',
                     'dcsvirtualdisk',
                     'dcsphysicaldisk',
                     'dcsdiskpath',
                     'dcsport',
                     'dcspoolmember']

    def get_domain(self):
        if hasattr(self, 'domain'):
            return self.domain
        buff = self.get_dcsservergroup()
        for line in buff.split('\n'):
            if not line.startswith('Alias'):
                continue
            self.domain = line.split(': ')[-1].strip()
            break
        if hasattr(self, 'domain'):
            return self.domain
        return "unknown"

    def dcscmd(self, cmd):
        return dcscmd(cmd, self.manager, self.username, self.password, dcs=self.name, conn=self.conn)

    def get_dcsservergroup(self):
        cmd = 'get-dcsservergroup -connection %s'%self.conn
        print("%s: %s"%(self.name, cmd))
        buff = self.dcscmd(cmd)[0]
        return buff

    def get_dcsserver(self):
        cmd = 'get-dcsserver -connection %s'%self.conn
        print("%s: %s"%(self.name, cmd))
        buff = self.dcscmd(cmd)[0]
        return buff

    def get_dcspool(self):
        cmd = 'get-dcspool -connection %s'%self.conn
        print("%s: %s"%(self.name, cmd))
        buff = self.dcscmd(cmd)[0]
        return buff

    def get_dcslogicaldisk(self):
        cmd = 'get-dcslogicaldisk -connection %s'%self.conn
        print("%s: %s"%(self.name, cmd))
        buff = self.dcscmd(cmd)[0]
        return buff

    def get_dcsvirtualdisk(self):
        cmd = 'get-dcsvirtualdisk -connection %s'%self.conn
        print("%s: %s"%(self.name, cmd))
        buff = self.dcscmd(cmd)[0]
        return buff

    def get_dcsphysicaldisk(self):
        cmd = 'get-dcsphysicaldisk -connection %s'%self.conn
        print("%s: %s"%(self.name, cmd))
        buff = self.dcscmd(cmd)[0]
        return buff

    def get_dcsdiskpath(self):
        cmd = 'get-dcsdiskpath -connection %s'%self.conn
        print("%s: %s"%(self.name, cmd))
        buff = self.dcscmd(cmd)[0]
        return buff

    def get_dcspoolmember(self):
        cmd = 'get-dcspoolmember -connection %s'%self.conn
        print("%s: %s"%(self.name, cmd))
        buff = self.dcscmd(cmd)[0]
        return buff

    def get_dcspoolperf(self):
        cmd = 'get-dcspool -connection %s | get-dcsperformancecounter -connection %s'%(self.conn, self.conn)
        print("%s: %s"%(self.name, cmd))
        buff = self.dcscmd(cmd)[0]
        return buff

    def get_dcslogicaldiskperf(self):
        cmd = 'get-dcslogicaldisk -connection %s | get-dcsperformancecounter -connection %s'%(self.conn, self.conn)
        print("%s: %s"%(self.name, cmd))
        buff = self.dcscmd(cmd)[0]
        return buff

    def get_dcsport(self):
        cmd = 'get-dcsport -connection %s'%self.conn
        print("%s: %s"%(self.name, cmd))
        buff = self.dcscmd(cmd)[0]
        return buff

    def add_vdisk(self, data):
        if 'disk_name' not in data:
            raise ex.excError("'disk_name' key is mandatory")
        if 'size' not in data:
            raise ex.excError("'size' key is mandatory")
        if 'paths' not in data:
            raise ex.excError("'paths' key is mandatory")

        data['disk_name'] = data['disk_name'] + '.1'
        l = data['paths'].split(',')
        paths = []
        for path in l:
            if 'iqn' in path:
                c, s = path.split('-iqn')
                s = 'iqn' + s
                paths.append((c, s))
            elif '-' in path:
                c, s = path.split('-')
                paths.append((c, s))
        if len(paths) == 0:
            raise ex.excError("no initiator to present to")

        pools = data['dg_name'].split(',')
        if len(pools) == 2:
            _pool1 = pools[0].split(':')
            _pool2 = pools[1].split(':')
            if len(_pool1) != 2 or len(_pool2) != 2:
                raise ex.excError("'dg_name' value is misformatted")
            d = {
              'disk_name': data['disk_name'],
              'size': data['size'],
              'sds1': _pool1[0],
              'sds2': _pool2[0],
              'pool1': _pool1[1],
              'pool2': _pool2[1],
              'conn': self.conn,
            }
            cmd = """$v = Add-DcsVirtualDisk -connection %(conn)s -Name "%(disk_name)s" -Size %(size)dGB  -EnableRedundancy -FirstServer %(sds1)s -FirstPool "%(pool1)s" -SecondServer %(sds2)s -SecondPool "%(pool2)s" ;""" % d
        elif len(pools) == 1:
            _pool1 = pools[0].split(':')
            if len(_pool1) != 2:
                raise ex.excError("'dg_name' value is misformatted")
            d = {
              'disk_name': data['disk_name'],
              'size': data['size'],
              'sds1': _pool1[0],
              'pool1': _pool1[1],
              'conn': self.conn,
            }
            cmd = """$v = Add-DcsVirtualDisk -connection %(conn)s -Name "%(disk_name)s" -Size %(size)dGB -Server %(sds1)s -Pool "%(pool1)s" ;""" % d
        else:
            raise ex.excError("'dg_name' value is misformatted")
        for machine in self.get_machines(map(lambda x: x[0], paths)):
            cmd += " $v | Serve-DcsVirtualDisk -connection %s -Machine %s -EnableRedundancy ;"""%(self.conn, machine)
        print(cmd)
        out, err, ret = self.dcscmd(cmd)

    def get_machines(self, ids):
        for i, id in enumerate(ids):
            if 'iqn' in id or ('-' in id and len(id) == 16):
                # iscsi or already in correct format
                continue
            # convert to dcs portname format
            id = list(id.upper())
            for j in (14, 12, 10, 8, 6, 4, 2):
                id.insert(j, '-')
            id = ''.join(id)
            ids[i] = id
        if not hasattr(self, "buff_dcsport"):
            self.buff_dcsport = self.get_dcsport()
        machines = set()
        for line in self.buff_dcsport.split('\n'):
            if line.startswith('HostId'):
                hostid = line.split(': ')[-1].strip()
            elif line.startswith('Id'):
                id = line.split(': ')[-1].strip()
                if id in ids:
                    machines.add(hostid)
        return machines

if __name__ == "__main__":
    o = Dcss()
    for dcs in o:
        print(dcs.get_dcsserver())
   0707010005c922000081b40000000000000000000000015ab2175400003381000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/rcDevTree.py   """
Top devices are bare disks or multipath paths.

Bottom devices are formatted devices or devices given to
applications like raw database devices.

A relation describes a parent-child link. A 'used' size can
be arbitrarily set on a relation : DevRelation.set_used()

A logical volume lv0 with segments on pv1 pv2 has two parent
relations : lv0-pv1 and lv0-pv2

"""
from forest import Forest
from converters import print_size
from rcGlobalEnv import rcEnv
from rcColor import color

try:
    from hashlib import md5
    def hash(s):
        o = md5()
        o.update(s.encode('utf-8'))
        return o.hexdigest()
except ImportError:
    from rcMd5 import md5
    def hash(s):
        return md5(s).digest().encode('hex')

class DevRelation(object):
    def __init__(self, parent, child, used=0):
        self.child = child
        self.parent = parent
        self.used = used
        self.used_set = False

    def set_used(self, used):
        self.used_set = True
        self.used = used

    def get_used(self, used):
        #
        # logical volumes and concatset need to set explicitly
        # the 'used' size the child consumes on the parent.
        #
        child = self.tree.get_dev(self.child)
        if used == 0:
            used = child.size
        if child.devtype is None:
            return used
        elif child.devtype in ("multipath", "linear", "partition", "extent"):
            return used
        elif child.devtype in ("raid0"):
            n = len(child.parents)
            return used/n
        elif child.devtype in ("raid1", "raid10"):
            n = len(child.parents)
            return used*2/n
        elif child.devtype in ("raid5"):
            n = len(child.parents)
            return used/(n-1)
        elif child.devtype in ("raid6"):
            n = len(child.parents)
            return used/(n-2)
        raise Exception("unknown devtype %s for %s"%(child.devtype, child.devname))

    def get_size(self, chain):
        if self.used_set:
            return self.used
        if len(chain) < 2:
            self.used = self.tree.get_dev(chain[-1].child).size
        else:
            self.used = chain[-2].used
        return self.used

class Dev(object):
    def __init__(self, devname, size, devtype):
        self.devname = devname
        self.devpath = []
        self.alias = devname
        self.size = size
        self.devtype = devtype

        # list of relations
        self.parents = []
        self.children = []

        self.removed = False

    def __iadd__(self, o):
        pass

    def remove(self, r):
        # to implement for each os
        r.log.info("remove method not implemented for device %s"%self.alias)

    def set_alias(self, alias):
        self.alias = alias

    def get_dev(self, devname):
        return self.tree.get_dev(devname)

    def get_size(self):
        return self.size

    def set_devtype(self, devtype):
        self.devtype = devtype

    def set_devpath(self, devpath):
        if devpath not in self.devpath:
            self.devpath.append(devpath)

    def get_child(self, devname):
        for r in self.children:
            if r.parent == devname:
                return r
        return None

    def get_parent(self, devname):
        for r in self.parents:
            if r.child == devname:
                return r
        return None

    def add_child(self, devname, size=0, devtype=None):
        r = self.get_child(devname)
        if r is None:
            r = self.tree.get_relation(self.devname, devname)
            if r is None:
                r = DevRelation(parent=self.devname, child=devname, used=size)
                r.tree = self.tree
            self.children.append(r)
        self.tree.add_dev(devname, size, devtype)
        return r

    def add_parent(self, devname, size=0, devtype=None):
        r = self.get_parent(devname)
        if r is None:
            r = self.tree.get_relation(devname, self.devname)
            if r is None:
                r = DevRelation(parent=devname, child=self.devname, used=size)
                r.tree = self.tree
            else:
                r.used = size
            self.parents.append(r)
        self.tree.add_dev(devname, size, devtype)
        return r

    def is_parent(self, devname):
        for r in self.children:
            if r.child == devname:
                return True
            d = self.get_dev(r.child)
            if d.is_parent(devname):
                return True
        return False

    def get_top_devs(self):
        if len(self.parents) == 0 or self.devtype == "multipath":
            return set([self])
        d = set()
        for parent in self.parents:
            dev = self.get_dev(parent.parent)
            d |= dev.get_top_devs()
        return d

    def get_top_devs_chain(self, chain=[]):
        if len(self.parents) == 0 or self.devtype == "multipath":
            return [[self, chain]]
        d = []
        for parent in self.parents:
            dev = self.get_dev(parent.parent)
            d += dev.get_top_devs_chain(chain+[parent])
        return d

    def print_dev(self, relation=None, node=None, highlight=[], verbose=False):
        if relation is None:
            parent_size = 0
        else:
            parent_size = self.get_dev(relation.parent).get_size()
        if parent_size == 0:
            pct = "-"
        else:
            pct = str(100*self.size//parent_size)+"%"

        node_dev = node.add_node()
        node_dev.add_column(self.alias, color.BROWN)
        node_dev.add_column(self.devtype)
        node_dev.add_column(print_size(self.size))
        node_dev.add_column(pct)
        if verbose:
            col = node_dev.add_column()
            for devpath in self.devpath:
                if highlight is not None and devpath in highlight:
                    textcolor = color.LIGHTBLUE
                else:
                    textcolor = None
                col.add_text(devpath, textcolor)

        for r in self.children:
            d = self.get_dev(r.child)
            if d is None:
                node_unk = node_dev.add_node()
                node_unk.add_column("%s (unknown)" % r.child)
            else:
                d.print_dev(relation=r, node=node_dev, highlight=highlight,
                            verbose=verbose)

    def print_dev_bottom_up(self, chain=[], node=None, highlight=[],
                            verbose=False):
        if len(chain) == 0:
            prev_size = 0
            used_s = "-"
        else:
            prev_size = self.tree.get_dev(chain[-1].child).size
            used = chain[-1].get_used(prev_size)
            used_s = print_size(used)
        if prev_size == 0:
            pct = "-"
        else:
            pct = str(100*used/self.size)+"%"

        node_dev = node.add_node()
        node_dev.add_column(self.alias, color.BROWN)
        node_dev.add_column(self.devtype)
        node_dev.add_column(used_s)
        node_dev.add_column(print_size(self.size))
        node_dev.add_column(pct)
        if verbose:
            col = node_dev.add_column()
            for devpath in self.devpath:
                if highlight is not None and devpath in highlight:
                    textcolor = color.LIGHTBLUE
                else:
                    textcolor = None
                col.add_text(devpath, textcolor)
        for r in self.parents:
            dev = self.get_dev(r.parent)
            dev.print_dev_bottom_up(chain+[r], node_dev, verbose=verbose)

    def get_parents_bottom_up(self, l=[]):
        for parent in self.parents:
            dev = self.get_dev(parent.parent)
            l.append(dev)
            l = dev.get_parents_bottom_up(l)
        return l

    def get_children_bottom_up(self):
        l = self.get_children_top_down()
        l.reverse()
        return l

    def get_children_top_down(self):
        l = []
        for child in self.children:
            dev = self.get_dev(child.child)
            l.append(dev)
            l += dev.get_children_top_down()
        return l

class DevTree(object):
    dev_class = Dev

    def __init__(self):
        self.dev = {}

        # root node of the relation tree
        self.root = []

    def __iadd__(self, o):
        if isinstance(o, Dev):
            o.tree = self
            self.dev[o.devname] = o
            if not self.has_relations(o.devname):
                r = DevRelation(parent=None, child=o.devname, used=o.size)
                r.tree = self
                self.root.append(r)
        return self

    def __str__(self):
        s = ""
        for r in self.root:
            s += self.dev[r.child].print_dev()
        return s

    def print_tree(self, devices=None, verbose=False):
        ftree = Forest()
        node = ftree.add_node()
        node.add_column(rcEnv.nodename, color.BOLD)
        node.add_column("Type", color.BOLD)
        node.add_column("Size", color.BOLD)
        node.add_column("Pct of Parent", color.BOLD)

        filtered = devices is not None and len(devices) > 0
        if filtered:
            devs = [self.get_dev_by_devpath(devpath) for devpath in devices]
        else:
            devs = [self.dev[r.child] for r in self.root]
        for dev in devs:
            if dev is None or (not filtered and dev.parents != []):
                continue
            dev.print_dev(node=node, highlight=devices, verbose=verbose)

        print(ftree)

    def print_tree_bottom_up(self, devices=None, verbose=False):
        ftree = Forest()
        node = ftree.add_node()
        node.add_column(rcEnv.nodename, color.BOLD)
        node.add_column("Type", color.BOLD)
        node.add_column("Parent Use", color.BOLD)
        node.add_column("Size", color.BOLD)
        node.add_column("Ratio", color.BOLD)

        if devices is None:
            devices = set()
        else:
            devices = set(devices)
        for dev in self.get_bottom_devs():
            if len(devices) > 0 and len(set(dev.devpath)&devices) == 0:
                continue
            dev.print_dev_bottom_up(node=node, highlight=devices,
                                    verbose=verbose)

        print(ftree)

    def has_relations(self, devname):
        l = []
        for r in self.root:
            if r.child == devname:
                return True
            d = self.get_dev(r.child)
            if d.is_parent(devname):
                return True
        return False

    def get_dev(self, devname):
        if devname not in self.dev:
            return None
        return self.dev[devname]

    def get_devs_by_devpaths(self, devpaths):
        devs = set()
        for devpath in devpaths:
            dev = self.get_dev_by_devpath(devpath)
            if dev is None:
                continue
            devs.add(dev)
        return devs

    def get_dev_by_devpath(self, devpath):
        for dev in self.dev.values():
            if devpath in dev.devpath:
                return dev

    def blacklist(self, dev):
        """ overload this fn with os specific implementation
        """
        return False

    def add_dev(self, devname, size=0, devtype=None):
        if devname in self.dev:
            return self.dev[devname]
        if self.blacklist(devname):
            return
        d = self.dev_class(devname, size, devtype)
        self += d
        return d

    def set_relation_used(self, parent, child, used):
        for d in self.dev.values():
            for r in d.children + d.parents:
                if parent == r.parent and child == r.child:
                    r.set_used(used)

    def get_relation(self, parent, child):
        for d in self.dev.values():
            for r in d.children + d.parents:
                if parent == r.parent and child == r.child:
                    return r
        return None

    def get_bottom_devs(self):
        return [self.dev[devname] for devname in self.dev if len(self.dev[devname].children) == 0]

    def get_top_devs(self):
        d = set()
        for dev in self.get_bottom_devs():
            d |= dev.get_top_devs()
        return list(d)

    def get_used(self, chain):
        used = 0
        for rel in chain:
            used = rel.get_used(used)
        return used

    def get_top_devs_usage_for_devpath(self, devpath):
        dev = self.get_dev_by_devpath(devpath)
        if dev is None:
            return []
        l = []
        for d, chain in dev.get_top_devs_chain():
            if len(chain) == 0:
                used = d.size
                region = 0
            else:
                used = self.get_used(chain)
                ref = self.get_dev(chain[0].child).alias
                region = hash(ref)
            l.append((d.devpath[0], used, region))
        return l

if __name__ == "__main__":
    tree = DevTree()
    d = tree.add_dev('/dev/sdb', 10000)
    d.add_child('/dev/sdb1', 8000)
    d.add_child('/dev/sdb2', 2000)
    d = tree.add_dev('/dev/sdc', 20000)
    d.add_child('/dev/mapper/vg01-foo', 1000)
    d = tree.get_dev('/dev/sdb2')
    d.add_child('/dev/mapper/vg01-foo', 1000)
    d = tree.get_dev('/dev/mapper/vg01-foo')
    d.add_child('foo.vmdk', 500)
    print(tree)
   0707010005c923000081b40000000000000000000000015ab21754000007f8000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/rcDevTreeAIX.py    import rcDevTree
import rcLvmAIX
import os
import re
from subprocess import *
from rcUtilities import which
from rcGlobalEnv import rcEnv

class DevTree(rcDevTree.DevTree):
    def load_lvm(self):
        lvm = rcLvmAIX.Lvm()
        for vg in lvm.vg.values():
            for lv in vg.lv.values():
                d = self.add_dev(lv.name, self.disk_size(lv.name), "linear")
                d.set_devpath('/dev/'+lv.name)
                for parentname in lv.pv_size:
                    size = lv.pv_size[parentname]
                    d.add_parent(parentname, size, "linear")
                    parent = self.get_dev(parentname)
                    parent.add_child(d.devname, size, "linear")

    def load_lsdev(self):
        if not which("lsdev"):
            return
        cmd = ["lsdev", "-Cc", "disk"]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode:
            return
        """
        hdisk0 Available  Virtual SCSI Disk Drive
        hdisk1 Available  Virtual SCSI Disk Drive
        """
        for line in out.split('\n'):
            if len(line) == 0:
                continue
            l = line.split()
            devname = l[0]
            d = self.add_dev(devname, self.disk_size(devname), "linear")
            d.set_devpath('/dev/'+devname)

    def disk_size(self, devname):
        cmd = ["bootinfo", "-s", devname]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode:
            return 0
        return int(out.strip())

    def load(self, di=None):
        self.load_lsdev()
        self.load_lvm()

    def blacklist(self, devname):
        bl = [r'^loop[0-9]*.*', r'^ram[0-9]*.*', r'^scd[0-9]*', r'^sr[0-9]*']
        for b in bl:
            if re.match(b, devname):
                return True
        return False

if __name__ == "__main__":
    tree = DevTree()
    tree.load()
    #print(tree)
    tree.print_tree_bottom_up()
    #print(map(lambda x: x.alias, tree.get_top_devs()))
0707010005c924000081b40000000000000000000000015ab21754000017e9000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/rcDevTreeHP-UX.py  import rcDevTree
import glob
import os
import re
from subprocess import *
from rcUtilities import which
from rcGlobalEnv import rcEnv

dim = __import__("rcDiskInfoHP-UX")
di = dim.diskInfo()

class DevTree(rcDevTree.DevTree):
    pe_size = {}

    def add_part(self, parent_devpath, child_devpath):
        child_dev = self.add_disk(child_devpath)
        if child_dev is None:
            return
        parent_dev = self.get_dev_by_devpath(parent_devpath)
        if parent_dev is None:
            return
        child_dev.add_parent(parent_dev.devname)
        parent_dev.add_child(child_dev.devname)

    def add_disk(self, devpath):
        devname = devpath.split('/')[-1]
        if devpath in self.lunmap:
            devtype = "multipath"
        else:
            devtype = "linear"
        size = di.disk_size(devpath)

        # exclude 0-sized md, Symmetrix gatekeeper and vcmdb
        if size in [0, 2, 30, 45]:
            return

        d = self.add_dev(devname, size, devtype)
        d.set_devpath(devpath)
        d.set_devpath(devpath.replace('/disk/', '/rdisk/').replace('/dsk/', '/rdsk/'))
        if devpath in self.lunmap:
            d.set_alias(self.lunmap[devpath]['wwid'])
        else:
            wwid = di.disk_id(devpath)
            if wwid != "":
                d.set_alias(wwid)
        return d

    def load_ioscan(self):
        if not which("/usr/sbin/ioscan"):
            return
        cmd = ["/usr/sbin/ioscan", "-FunNC", "disk"]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode:
            if "illegal option -- N" not in err:
                return
            cmd = ["/usr/sbin/ioscan", "-FunC", "disk"]
            p = Popen(cmd, stdout=PIPE, stderr=PIPE)
            out, err = p.communicate()
            if p.returncode:
                return
        """
        scsi:wsio:T:T:F:31:188:0:disk:sdisk:0/2/1/0.0.0.0.0:0 0 5 18 0 0 0 0 195 124 63 185 173 253 214 203 :0:root.sba.lba.sasd.sasd_vbus.tgt.sdisk:sdisk:CLAIMED:DEVICE:HP      DG146BB976:0:
                      /dev/disk/disk11      /dev/disk/disk11_p2   /dev/rdisk/disk11     /dev/rdisk/disk11_p2
                      /dev/disk/disk11_p1   /dev/disk/disk11_p3   /dev/rdisk/disk11_p1  /dev/rdisk/disk11_p3
        """
        for w in out.split():
            if not w.startswith('/dev/'):
                new = True
                continue
            if new:
                disk = w
                new = False
                d = self.add_disk(disk)
                continue
            if d is None or '/rdisk/' in w:
                continue
            elif '/pt/' in w:
                d.set_devpath(w)
            elif '_p' in w:
                self.add_part(disk, w)
            else:
                # arbitrary dsf alias
                d.set_devpath(w)

    def get_lunmap(self):
        if hasattr(self, "lunmap"):
            return
        self.lunmap = {}
        if not which("scsimgr"):
            return
        cmd = ["scsimgr", "lun_map"]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode:
            return
        """

        LUN PATH INFORMATION FOR LUN : /dev/rdisk/disk10

Total number of LUN paths     = 1
World Wide Identifier(WWID)    = 0x5000c5000aba6793

LUN path : lunpath0
Class                         = lunpath
Instance                      = 0
Hardware path                 = 0/2/1/0.0x5000c5000aba6791.0x0
SCSI transport protocol       = sas
State                         = UNOPEN
Last Open or Close state      = ACTIVE
"""
        for line in out.split('\n'):
            if "INFORMATION" in line:
                disk = line.split()[-1]
                self.lunmap[disk] = {}
            if "WWID" in line:
                wwid = line.split()[-1].replace('0x', '')
                if wwid != "=":
                    self.lunmap[disk]['wwid'] = line.split()[-1].replace('0x', '')
                else:
                    del(self.lunmap[disk])

    def load_lv(self, lv):
        if not which("lvdisplay"):
            return
        cmd = ["lvdisplay", "-v", lv]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode:
            return

        vgname = lv.split('/')[2]

        # parser
        h = {}
        for line in out.split('\n'):
            line = line.strip()
            if 'LV Size' in line:
                size = int(line.split()[-1])
            if not line.startswith('/dev'):
                continue
            pv, le, pe = line.split()
            h[pv] = int(pe) * self.pe_size[vgname]

        # use the linux lvm naming convention
        devname = lv.replace('/dev/','').replace('-','--').replace('/','-')
        d = self.add_dev(devname, size, "linear")

        for pv in h:
            d.add_parent(pv.replace('/dev/disk/', ''), size=h[pv])
            d.set_devpath(lv)
            parent_dev = self.get_dev_by_devpath(pv)
            if parent_dev is None:
                continue
            parent_dev.add_child(d.devname)

    def load_lvm(self):
        if not which("vgdisplay"):
            return
        cmd = ["vgdisplay", "-v"]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        for line in out.split('\n'):
            if 'VG Name' in line:
                vgname = line.split()[-1].replace('/dev/','')
            if 'PE Size' in line:
                self.pe_size[vgname] = int(line.split()[-1])
            if 'LV Name' not in line:
                continue
            self.load_lv(line.split()[-1])

    def load(self, di=None):
        self.get_lunmap()
        self.load_ioscan()
        self.load_lvm()

    def blacklist(self, devname):
        bl = [r'^loop[0-9]*.*', r'^ram[0-9]*.*', r'^scd[0-9]*', r'^sr[0-9]*']
        for b in bl:
            if re.match(b, devname):
                return True
        return False

if __name__ == "__main__":
    tree = DevTree()
    tree.load()
    #print(tree)
    tree.print_tree_bottom_up()
    #print(map(lambda x: x.alias, tree.get_top_devs()))
   0707010005c925000081b40000000000000000000000015ab2175400004f70000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/rcDevTreeLinux.py  from __future__ import division
import rcDevTree
import glob
import os
import re
import math
from subprocess import *
from rcUtilities import which
from rcGlobalEnv import rcEnv
import rcDevTreeVeritas
import rcExceptions as ex

class Dev(rcDevTree.Dev):
    def remove_loop(self, r):
        cmd = [rcEnv.syspaths.losetup, "-d", self.devpath[0]]
        ret, out, err = r.vcall(cmd)
        if ret != 0:
            raise ex.excError(err)
        self.removed = True

    def remove_dm(self, r):
        cmd = [rcEnv.syspaths.dmsetup, "remove", self.alias]
        ret, out, err = r.vcall(cmd)
        if ret != 0:
            raise ex.excError(err)
        self.removed = True

    def remove(self, r):
        if self.removed:
            return
        if self.devname.startswith("loop"):
            return self.remove_loop(r)
        if self.devname.startswith("dm-"):
            return self.remove_dm(r)

class DevTree(rcDevTreeVeritas.DevTreeVeritas, rcDevTree.DevTree):
    di = None
    dev_h = {}
    dev_class = Dev

    def get_size(self, devpath):
        size = 0
        try:
            with open(devpath+'/size', 'r') as f:
                size = int(f.read().strip()) // 2048
        except:
            pass
        return size

    def get_dm(self):
        if hasattr(self, 'dm_h'):
            return self.dm_h
        self.dm_h = {}
        self._dm_h = {}
        if not os.path.exists("/dev/mapper"):
            return self.dm_h
        try:
            cmd = [rcEnv.syspaths.dmsetup, 'mknodes']
            p = Popen(cmd, stdout=PIPE, stderr=PIPE)
            p.communicate()
        except:
            # best effort
            pass
        devpaths = glob.glob("/dev/mapper/*")
        if '/dev/mapper/control' in devpaths:
            devpaths.remove('/dev/mapper/control')
        for devpath in devpaths:
            try:
                s = os.stat(devpath)
            except OSError:
                continue
            minor = os.minor(s.st_rdev)
            self.dm_h[devpath.replace("/dev/mapper/", "")] = "dm-%d"%minor

        # reverse hash
        for mapname, devname in self.dm_h.items():
            self._dm_h[devname] = mapname

        return self.dm_h

    def get_map_wwid(self, map):
        if not which(rcEnv.syspaths.multipath):
            return None
        if not hasattr(self, 'multipath_l'):
            self.multipath_l = []
            cmd = [rcEnv.syspaths.multipath, '-l']
            p = Popen(cmd, stdout=PIPE, stderr=PIPE)
            out, err = p.communicate()
            if p.returncode != 0:
                return None
            self.multipath_l = out.decode().splitlines()
        for line in self.multipath_l:
            if not line.startswith(map):
                continue
            try:
                wwid = line[line.index('(')+2:line.index(')')]
            except ValueError:
                wwid = line.split()[0]
            return wwid
        return None

    def get_wwid(self):
        if hasattr(self, 'wwid_h'):
            return self.wwid_h
        self.wwid_h = {}
        self.wwid_h.update(self.get_wwid_native())
        self.wwid_h.update(self.get_mp_powerpath())
        return self.wwid_h

    def get_wwid_native(self):
        if not which(rcEnv.syspaths.multipath):
            return self.wwid_h
        cmd = [rcEnv.syspaths.multipath, '-l']
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            return self.wwid_h
        for line in out.decode().splitlines():
            if 'dm-' not in line:
                continue
            devname = line[line.index('dm-'):].split()[0]
            try:
                wwid = line[line.index('(')+2:line.index(')')]
            except ValueError:
                wwid = line.split()[0][1:]
            self.wwid_h[devname] = wwid
        return self.wwid_h

    def get_mp(self):
        if hasattr(self, 'mp_h'):
            return self.mp_h
        self.mp_h = {}
        self.mp_h.update(self.get_mp_native())
        self.mp_h.update(self.get_mp_powerpath())
        return self.mp_h

    def get_mp_powerpath(self):
        self.powerpath = {}
        if not which("powermt"):
            return {}
        cmd = ['powermt', 'display', 'dev=all']
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            return {}
        lines = out.decode().splitlines()
        if len(lines) < 1:
            return {}
        dev = None
        paths = []
        mp_h = {}
        for line in lines:
            if len(line) == 0:
                # new mpath
                # - store previous
                # - reset path counter
                if dev is not None:
                    if len(paths) > 0:
                        did = self.di.disk_id(paths[0])
                    mp_h[name] = did
                    self.powerpath[name] = paths
                    dev = None
                    paths = []
            if 'Pseudo name' in line:
                l = line.split('=')
                if len(l) != 2:
                    continue
                name = l[1]
                dev = "/dev/"+name
            else:
                l = line.split()
                if len(l) < 3:
                    continue
                if l[2].startswith("sd"):
                    paths.append("/dev/"+l[2])
        return mp_h

    def get_mp_native(self):
        if not which(rcEnv.syspaths.dmsetup):
            return {}
        cmd = [rcEnv.syspaths.dmsetup, 'ls', '--target', 'multipath']
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            return {}
        mp_h = {}
        for line in out.decode().splitlines():
            l = line.split()
            if len(l) == 0:
                continue
            mapname = l[0]
            major = l[1].strip('(,')
            minor = l[2].strip(' )')
            mp_h['dm-'+minor] = mapname
        return mp_h

    def get_md(self):
        if hasattr(self, 'md_h'):
            return self.md_h
        fpath = "/proc/mdstat"
        self.md_h = {}
        try:
            with open(fpath, 'r') as f:
                buff = f.read()
        except:
            return self.md_h
        for line in buff.split('\n'):
            if line.startswith("Personalities"):
                continue
            if len(line) == 0 or line[0] == " ":
                continue
            l = line.split()
            if len(l) < 4:
                continue
            self.md_h[l[0]] = l[3]
        return self.md_h

    def load_dm_dev_t(self):
        table = {}
        if not which(rcEnv.syspaths.dmsetup):
            return
        cmd = [rcEnv.syspaths.dmsetup, 'ls']
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            return
        for line in out.decode().splitlines():
            l = line.split()
            if len(l) == 0:
                continue
            mapname = l[0]
            major = l[1].strip('(,')
            minor = l[2].strip(' )')
            dev_t = ':'.join((major, minor))
            self.dev_h[dev_t] = mapname

    def load_dm(self):
        table = {}
        self.load_dm_dev_t()
        if not which(rcEnv.syspaths.dmsetup):
            return
        cmd = [rcEnv.syspaths.dmsetup, 'table']
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            return
        for line in out.decode().splitlines():
            l = line.split()
            if len(l) < 5:
                continue
            mapname = l[0].strip(':')
            size = int(math.ceil(1.*int(l[2])*512/1024/1024))
            maptype = l[3]

            if maptype == "multipath" and size in [0, 2, 3, 30, 45]:
                continue

            for w in l[4:]:
                if ':' not in w:
                    continue
                if mapname not in table:
                    table[mapname] = {"devs": [], "size": 0, "type": "linear"}
                table[mapname]["devs"].append(w)
                table[mapname]["size"] += size
                table[mapname]["type"] = maptype
        for mapname in table:
            d = self.add_dev(mapname, table[mapname]["size"])
            d.set_devtype(table[mapname]["type"])
            d.set_devpath('/dev/mapper/'+mapname)

            s = mapname.replace('--', ':').replace('-', '/').replace(':','-')
            if "/" in s:
                d.set_devpath('/dev/'+s)
            wwid = self.get_map_wwid(mapname)
            if wwid is not None:
                d.set_alias(wwid)
            for dev in table[mapname]["devs"]:
                if dev not in self.dev_h:
                    continue
                d.add_parent(self.dev_h[dev])
                parentdev = self.get_dev(self.dev_h[dev])
                parentdev.add_child(mapname)

    def set_udev_symlink(self, d, name):
        if not which("udevadm"):
            return
        cmd = ["/sbin/udevadm", "info", "-q", "symlink", "--name", name]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            return
        for s in out.decode().split():
            d.set_devpath("/dev/"+s)

    def get_lv_linear(self):
        if hasattr(self, 'lv_linear'):
            return self.lv_linear
        self.lv_linear = {}
        if not which(rcEnv.syspaths.dmsetup):
            return self.lv_linear
        cmd = [rcEnv.syspaths.dmsetup, 'table', '--target', 'linear']
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            return self.lv_linear
        for line in out.decode().splitlines():
            l = line.split(':')
            if len(l) < 2:
                continue
            mapname = l[0]
            line = line[line.index(':')+1:]
            l = line.split()
            if len(l) < 3:
                continue
            length = int(l[1])*512/1024/1024
            devt = l[3]
            if mapname in self.lv_linear:
                self.lv_linear[mapname].append((devt, length))
            else:
                self.lv_linear[mapname] = [(devt, length)]
        return self.lv_linear

    def is_cdrom(self, devname):
        p = '/sys/block/%s/device/media'%devname
        if not os.path.exists(p):
            return False
        with open(p, 'r') as f:
            buff = f.read()
        if buff.strip() == "cdrom":
            return True
        return False

    def get_loop(self):
        self.loop = {}
        cmd = [rcEnv.syspaths.losetup]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            return
        for line in out.decode().splitlines():
            if not line.startswith("/"):
                continue
            l = line.split()
            if len(l) < 2:
                continue
            loop = l[0].replace("/dev/", "")
            for idx in range(1, len(l)):
                fpath = l[-idx]
                if fpath.startswith("/"):
                    break
            self.loop[loop] = fpath

    def dev_type(self, devname):
        t = "linear"
        md_h = self.get_md()
        mp_h = self.get_mp()
        if devname in md_h:
            return md_h[devname]
        if devname in mp_h:
            return "multipath"
        return t

    def add_loop_relations(self):
        self.get_loop()
        from rcMountsLinux import Mounts
        m = Mounts()
        for devname, fpath in self.loop.items():
            if fpath == "(deleted)":
                continue
            parentpath = m.get_fpath_dev(fpath)
            if parentpath is None:
                continue
            d = self.get_dev_by_devpath(parentpath)
            if d is None:
                continue
            d.add_child(devname)
            c = self.get_dev(devname)
            r = c.add_parent(d.devname, size=c.size)

    def add_drbd_relations(self):
        if not which("drbdadm") or not os.path.exists('/proc/drbd'):
            return
        cmd = ["drbdadm", "dump-xml"]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            return
        from xml.etree import ElementTree as etree
        tree = etree.fromstring(out.decode())
        for res in tree.getiterator('resource'):
            for host in res.findall('host'):
                if host.attrib['name'] != rcEnv.nodename:
                    continue
                edisk = host.find('disk')
                edev = host.find('device')
                if edisk is None or edev is None:
                    edisk = host.find('volume/disk')
                    edev = host.find('volume/device')
                if edisk is None or edev is None:
                    continue
                devname = 'drbd'+edev.attrib['minor']
                parentpath = edisk.text
                d = self.get_dev_by_devpath(parentpath)
                if d is None:
                    continue
                d.add_child(devname)
                c = self.get_dev(devname)
                c.add_parent(d.devname)

    def load_dev(self, devname, devpath):
        if self.is_cdrom(devname):
            return

        mp_h = self.get_mp()
        wwid_h = self.get_wwid()
        size = self.get_size(devpath)

        # exclude 0-sized md, Symmetrix gatekeeper and vcmdb
        if devname in self.mp_h and size in (0, 2, 30, 45):
            return

        devtype = self.dev_type(devname)
        d = self.add_dev(devname, size, devtype)

        if d is None:
            return

        self.set_udev_symlink(d, devname)
        self.get_dm()

        if 'cciss' in devname:
            d.set_devpath('/dev/'+devname.replace('!', '/'))
        elif devname in self.mp_h:
            if devname in self._dm_h:
                d.set_devpath('/dev/mpath/'+self._dm_h[devname])
                d.set_devpath('/dev/'+devname)
        else:
            d.set_devpath('/dev/'+devname)

        # store devt
        try:
            with open("%s/dev"%devpath, 'r') as f:
                devt = f.read().strip()
                self.dev_h[devt] = devname
        except IOError:
            pass

        # add holders
        for holderpath in glob.glob("%s/holders/*"%devpath):
            holdername = os.path.basename(holderpath)
            if not os.path.exists(holderpath):
                # broken symlink
                continue
            size = self.get_size(holderpath)
            devtype = self.dev_type(holdername)
            d.add_child(holdername, size, devtype)

        # add lv aliases
        if devname in self._dm_h:
            alias = self._dm_h[devname]
            d.set_alias(alias)
            d.set_devpath('/dev/mapper/'+alias)
            s = alias.replace('--', ':').replace('-', '/').replace(':','-')
            d.set_devpath('/dev/'+s)

        # add slaves
        for slavepath in glob.glob("%s/slaves/*"%devpath):
            slavename = os.path.basename(slavepath)
            if not os.path.exists(slavepath):
                # broken symlink
                continue
            size = self.get_size(slavepath)
            devtype = self.dev_type(slavename)
            d.add_parent(slavename, size, devtype)

        if devname in wwid_h:
            wwid = wwid_h[devname]
            d.set_alias(wwid)
            try:
                p = glob.glob('/dev/mpath/?'+wwid)[0]
                d.set_devpath(p)
            except:
                pass

        return d

    def get_dev_t(self, dev):
        major, minor = self._get_dev_t(dev)
        return ":".join((str(major), str(minor)))

    def _get_dev_t(self, dev):
        try:
            s = os.stat(dev)
            minor = os.minor(s.st_rdev)
            major = os.major(s.st_rdev)
        except:
            return 0, 0
        return major, minor

    def load_fdisk(self):
        self.get_wwid()
        os.environ["LANG"] = "C"
        p = Popen(["fdisk", "-l"], stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            return
        for line in out.decode().splitlines():
            if line.startswith('/dev/dm-'):
                continue
            elif line.startswith("Disk "):
                # disk
                devpath = line.split()[1].strip(':')
                if devpath.startswith('/dev/dm-'):
                    continue
                size = int(line.split()[-2]) / 1024 / 1024
                if size in [2, 3, 30, 45]:
                    continue
                devname = devpath.replace('/dev/','').replace("/","!")
                devtype = self.dev_type(devname)
                dev_t = self.get_dev_t(devpath)
                self.dev_h[dev_t] = devname
                d = self.add_dev(devname, size, devtype)
                if d is None:
                    continue
                d.set_devpath(devpath)
                if devname.startswith('emc') and devname in self.wwid_h:
                    d.set_alias(self.wwid_h[devname])
                    for path in self.powerpath[devname]:
                        p = self.add_dev(path.replace('/dev/',''), size, "linear")
                        p.set_devpath(path)
                        p.add_child(devname)
                        d.add_parent(path.replace('/dev/',''))
            elif line.startswith('Unit'):
                unit = int(line.split()[-2])
            elif line.startswith('/dev/'):
                # partition
                line = line.replace('*', '')
                _l = line.split()
                partpath = _l[0]
                partend = int(_l[2])
                partstart = int(_l[1])
                partsize = (partend - partstart) * unit / 1024/1024
                partname = partpath.replace('/dev/','').replace("/","!")
                dev_t = self.get_dev_t(partpath)
                self.dev_h[dev_t] = partname
                p = self.add_dev(partname, partsize, "linear")
                if p is None:
                    continue
                p.set_devpath(partpath)
                d.add_child(partname)
                p.add_parent(devname)

    def load_sysfs(self):
        for devpath in glob.glob("/sys/block/*"):
            devname = os.path.basename(devpath)
            if devname.startswith("Vx"):
                continue
            d = self.load_dev(devname, devpath)

            if d is None:
                continue

            # add parts
            for partpath in glob.glob("%s/%s*"%(devpath, devname)):
                partname = os.path.basename(partpath)
                p = self.load_dev(partname, partpath)
                if p is None:
                    continue
                d.add_child(partname)
                p.add_parent(devname)

    def tune_lv_relations(self):
        dm_h = self.get_dm()
        for lv, segments in self.get_lv_linear().items():
            for devt, length in segments:
                if devt not in self.dev_h:
                    continue
                if lv not in dm_h:
                    continue
                child = dm_h[lv]
                parent = self.dev_h[devt]
                r = self.get_relation(parent, child)
                if r is not None:
                    r.set_used(length)

    def load(self, di=None):
        if di is not None:
            self.di = di
        if self.di is None:
            from rcDiskInfoLinux import diskInfo
            self.di = diskInfo()

        if len(glob.glob("/sys/block/*/slaves")) == 0:
            self.load_fdisk()
            self.load_dm()
        else:
            self.load_sysfs()
            self.tune_lv_relations()

        self.load_vx_dmp()
        self.load_vx_vm()
        self.add_drbd_relations()
        self.add_loop_relations()

    def blacklist(self, devname):
        bl = [r'^ram[0-9]*.*', r'^scd[0-9]*', r'^sr[0-9]*']
        for b in bl:
            if re.match(b, devname):
                return True
        return False

if __name__ == "__main__":
    tree = DevTree()
    tree.load()
    #print(tree)
    tree.print_tree_bottom_up()
    #print(map(lambda x: x.alias, tree.get_top_devs()))
0707010005c926000081b40000000000000000000000015ab2175400000c00000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/rcDevTreeOSF1.py   import rcDevTree
import os
import re
from rcUtilities import which, justcall
from rcGlobalEnv import rcEnv
from rcDiskInfoOSF1 import diskInfo

regex = re.compile('\W*[a-h]:')

class DevTree(rcDevTree.DevTree):
    def get_parts(self, devname, d):
        if d is None:
            return
        data = self.di.h[devname]
        cmd = ['disklabel', '/dev/rdisk/'+devname+'a']
        out, err, ret = justcall(cmd)
        """
# /dev/rdisk/dsk15a:
type: SCSI
disk: HSV210
label:
flags:
bytes/sector: 512
sectors/track: 128
tracks/cylinder: 128
sectors/cylinder: 16384
cylinders: 15360
sectors/unit: 251658240
rpm: 3600
interleave: 1
trackskew: 7
cylinderskew: 26
headswitch: 0		# milliseconds
track-to-track seek: 0	# milliseconds
drivedata: 0

8 partitions:
#            size       offset    fstype  fsize  bsize   cpg  # ~Cyl values
  a:       131072            0    unused      0      0        #      0 - 7
  b:       262144       131072    unused      0      0        #      8 - 23
  c:    251658240            0     AdvFS                      #      0 - 15359
  d:            0            0    unused      0      0        #      0 - 0
  e:            0            0    unused      0      0        #      0 - 0
  f:            0            0    unused      0      0        #      0 - 0
  g:    125632512       393216    unused      0      0        #     24 - 7691
  h:    125632512    126025728    unused      0      0        #   7692 - 15359
"""
        if ret != 0:
            return
        for line in out.split("\n"):
            if line.startswith("bytes/sector"):
                bs = int(line.split()[-1])
            if regex.match(line) is None:
                continue
            l = line.split()
            part = l[0].replace(':','')
            size = 1. * int(l[1]) * bs / 1024 / 1024
            size = int(size)

            partname = devname+part
            child_dev = self.add_dev(partname, size, "linear")
            child_dev.set_devpath('/dev/disk/'+partname)
            child_dev.set_devpath('/dev/rdisk/'+partname)
            if child_dev is None:
                continue
            child_dev.add_parent(devname)
            d.add_child(partname)

    def get_disks(self):
        for devname in self.di.h:
            if self.di.h[devname]['size'] in [0, 2, 30, 45]:
                continue
            d = self.add_dev(devname, self.di.h[devname]['size'], "linear")
            d.set_alias(self.di.h[devname]['wwid'])
            d.set_devpath('/dev/rdisk/'+devname)
            d.set_devpath('/dev/disk/'+devname)
            self.get_parts(devname, d)

    def load(self, di=None):
        self.di = diskInfo()
        self.get_disks()

    def blacklist(self, devname):
        bl = [r'^loop[0-9]*.*', r'^ram[0-9]*.*', r'^scd[0-9]*', r'^sr[0-9]*']
        for b in bl:
            if re.match(b, devname):
                return True
        return False

if __name__ == "__main__":
    tree = DevTree()
    tree.load()
    #print(tree)
    tree.print_tree_bottom_up()
    #print(map(lambda x: x.alias, tree.get_top_devs()))
0707010005c927000081b40000000000000000000000015ab2175400002881000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/rcDevTreeSunOS.py  import rcDevTree
import glob
import os
import re
from subprocess import *
from rcUtilities import which
from rcGlobalEnv import rcEnv
import rcDevTreeVeritas

class DevTree(rcDevTreeVeritas.DevTreeVeritas, rcDevTree.DevTree):
    di = None
    zpool_members = {}
    zpool_used = {}
    zpool_used_zfs = {}
    zpool_size = {}
    zpool_datasets = {}
    zpool_datasets_used = {}

    def load_partitions(self, d):
        """
        *                          First     Sector    Last
        * Partition  Tag  Flags    Sector     Count    Sector  Mount Directory
               0      2    00   16779312  54281421  71060732
               1      3    01          0  16779312  16779311
               2      5    00          0  71127180  71127179
               7      0    00   71060733     66447  71127179
        """
        p = Popen(["prtvtoc", d.devpath[0]], stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            return
        for line in out.split("\n"):
            line = line.strip()
            if line.startswith('*'):
                continue
            if line.startswith('2'):
                continue
            l = line.split()
            if len(l) < 6:
                continue
            partname = d.devname + 's' + l[0]
            partpath = d.devpath[0][:-1] + l[0]
            partsize = self.di.get_part_size(partpath)
            p = self.add_dev(partname, partsize, "linear")
            p.set_devpath(partpath)
            self.add_device_devpath(p, partpath)
            p.set_devpath(partpath.replace("/dev/rdsk/", "/dev/dsk/"))
            self.add_device_devpath(p, partpath)
            d.add_child(partname)
            p.add_parent(d.devname)

    def add_device_devpath(self, dev, path):
        if os.path.islink(path):
            altpath = os.path.realpath(path)
            if altpath != path:
                dev.set_devpath(altpath)

    def load_disks(self):
        self.load_vxdisk_cache()
        if len(self.vxdisk_cache) > 0:
            self.load_vxdisk()
        else:
            self.load_format()

    def load_vxdisk(self):
        for devpath, data in self.vxdisk_cache.items():
            if "size" not in data or "devpath" not in data:
                continue
            devname = os.path.basename(devpath)
            bdevpath = devpath.replace("/rdsk/", "/dsk/").replace("/rdmp/", "/dmp/")
            size = data["size"]
            d = self.add_dev(devname, size, "linear")
            d.set_devpath(data["devpath"])
            d.set_devpath(devpath)
            d.set_devpath(bdevpath)
            self.add_device_devpath(d, devpath)
            self.add_device_devpath(d, bdevpath)
            self.load_partitions(d)

    def load_format(self):
        """
        0. c3t0d0 <SUN36G cyl 24620 alt 2 hd 27 sec 107>
          /pci@1f,700000/scsi@2/sd@0,0
        4. c5t600508B4000971CD00010000024A0000d0 <HP-HSV210-6200 cyl 61438 alt 2 hd 128 sec 128>  EVA_SAVE
          /scsi_vhci/ssd@g600508b4000971cd00010000024a0000
        """
        p = Popen(["format", "-e"], stdout=PIPE, stderr=PIPE, stdin=PIPE)
        out, err = p.communicate(input=None)
        for line in out.split("\n"):
            line = line.strip()
            if re.match(r"[0-9]+\. ", line) is None:
                continue
            l = line.split()
            devname = l[1]
            devpath = '/dev/rdsk/'+devname+'s2'
            bdevpath = devpath.replace("/rdsk/", "/dsk/")
            size = self.di.get_size(devpath)
            d = self.add_dev(devname, size, "linear")
            d.set_devpath(devpath)
            d.set_devpath(bdevpath)
            self.add_device_devpath(d, devpath)
            self.add_device_devpath(d, bdevpath)
            self.load_partitions(d)

    def load_sds(self):
        if not os.path.exists("/usr/sbin/metastat"):
            return
        p = Popen(["metastat", "-p"], stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            return
        lines = out.split('\n')
        lines.reverse()

        """
        # metastat -p
        d11 -m d2 d3 1
        d2 1 1 c3t0d0s1
        d3 1 1 c3t1d0s1
        """
        for line in lines:
            l = line.split()
            if len(l) < 3:
                continue
            childname = l[0]
            childpath = "/dev/md/dsk/"+childname
            childsize = self.di.get_size(childpath)
            if l[1] == "-m":
                childtype = "raid1"
            else:
                childtype = "linear"
            childdev = self.add_dev(childname, childsize, childtype)
            childdev.set_devpath(childpath)
            if l[1] == "-m":
                parentnames = l[2:-1]
            else:
                parentnames = [l[-1]]

            for parentname in parentnames:
                parentpath = "/dev/md/dsk/"+parentname
                parentsize = self.di.get_size(parentpath)
                parentdev = self.add_dev(parentname, parentsize, "linear")
                childdev.add_parent(parentname)
                parentdev.add_child(childname)

    def load_zpool(self):
        p = Popen(["zpool", "list", "-H"], stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            return
        for line in out.split('\n'):
            l = line.split()
            if len(l) == 0:
                continue
            poolname = l[0]
            self.load_zpool1(poolname)

    def load_zpool1(self, poolname):
        p = Popen(["zpool", "status", poolname], stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            return
        self.zpool_members[poolname] = []
        for line in out.split('\n'):
            l = line.split()
            if len(l) != 5:
                continue
            if l[0] == 'NAME':
                continue
            if l[0] == poolname:
                continue
            devname = l[0]

            # -d mode import ?
            if devname.startswith(rcEnv.paths.pathvar):
                devname = devname.split('/')[-1]
            d = self.get_dev(devname)
            if d is None:
                continue
            self.zpool_members[poolname].append(d)

        p = Popen(["zpool", "iostat", poolname], stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            return
        lines = out.split('\n')
        lines = [l for l in lines if len(l) > 0]
        self.zpool_used[poolname] = self.read_size(lines[-1].split()[1])
        zpool_free = self.read_size(lines[-1].split()[2])
        self.zpool_size[poolname] = self.zpool_used[poolname] + zpool_free

        p = Popen(["zfs", "list", "-H", "-r", "-t", "filesystem", poolname], stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            return
        self.zpool_datasets[poolname] = []
        self.zpool_datasets_used[poolname] = 0
        for line in out.split('\n'):
            l = line.split()
            if len(l) == 0:
                continue
            zfsname = l[0]
            size = self.read_size(l[1])
            refer = self.read_size(l[3])
            size -= refer
            mnt = l[4]
            if zfsname == poolname:
                self.zpool_used_zfs[poolname] = size
                continue
            self.zpool_datasets[poolname].append((zfsname, size))
            self.zpool_datasets_used[poolname] += size

        p = Popen(["zfs", "list", "-H", "-t", "snapshot"], stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            return
        for line in out.split('\n'):
            l = line.split()
            if len(l) == 0:
                continue
            zfsname = l[0]
            if not zfsname.startswith(poolname+'/') and \
               not zfsname.startswith(poolname+'@'):
                continue
            size = self.read_size(l[1])
            #refer = self.read_size(l[3])
            self.zpool_datasets[poolname].append((zfsname, size))
            self.zpool_datasets_used[poolname] += size

        rest = self.zpool_used_zfs[poolname] - self.zpool_datasets_used[poolname]
        if rest < 0:
            rest = 0
        self.zpool_datasets[poolname].append((poolname, rest))
        self.zpool_datasets_used[poolname] += rest

        if self.zpool_datasets_used[poolname] == 0:
            ratio = 0
        else:
            ratio = 1.0 * self.zpool_used[poolname] / self.zpool_datasets_used[poolname]

        for zfsname, size in self.zpool_datasets[poolname]:
            used = int(size*ratio)
            d = self.add_dev(zfsname, used, "zfs")
            d.set_devpath(zfsname)
            for m in self.zpool_members[poolname]:
                member_ratio = 1.0 * m.size / self.zpool_size[poolname]
                d.add_parent(m.devname)
                m.add_child(zfsname)
                self.set_relation_used(m.devname, zfsname, int(used*member_ratio))

    def read_size(self, s):
        if s == '0':
            return 0
        unit = s[-1]
        size = float(s[:-1].replace(',','.'))
        if unit == 'K':
            size = size / 1024
        elif unit == 'M':
            pass
        elif unit == 'G':
            size = size * 1024
        elif unit == 'T':
            size = size * 1024 * 1024
        elif unit == 'P':
            size = size * 1024 * 1024 * 1024
        elif unit == 'Z':
            size = size * 1024 * 1024 * 1024 * 1024
        else:
            raise Exception("unit not supported: %s"%unit)
        return int(size)

    def load(self, di=None):
        if di is not None:
            self.di = di
        if self.di is None:
            from rcDiskInfoSunOS import diskInfo
            self.di = diskInfo(deferred=True)
        self.load_disks()
        self.load_zpool()
        self.load_sds()
        self.load_vx_dmp()
        self.load_vx_vm()

    def blacklist(self, devname):
        bl = [r'^loop[0-9]*.*', r'^ram[0-9]*.*', r'^scd[0-9]*', r'^sr[0-9]*']
        for b in bl:
            if re.match(b, devname):
                return True
        return False

if __name__ == "__main__":
    tree = DevTree()
    tree.load()
    #print(tree)
    tree.print_tree_bottom_up()
    #print(map(lambda x: x.alias, tree.get_top_devs()))
   0707010005c928000081b40000000000000000000000015ab2175400001ff5000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/rcDevTreeVeritas.py    import glob
import os
from subprocess import *
from rcUtilities import which, justcall
import rcDevTree
from rcGlobalEnv import rcEnv

class DevTreeVeritas(rcDevTree.DevTree):
    vxprint_cache = {}
    vxdisk_cache = {}

    def vx_get_size(self, name):
        _dg, _vt = name.split("/")
        out = self.vxprint(_dg)
        lines = out.split("\n")
        lines.reverse()
        size = 0
        for line in lines:
            l = line.split()
            if len(l) < 5:
                continue
            if l[0] == "v":
                name = l[1]
                if l[2] == _vt or l[1] == _vt:
                    size += int(float(l[5].rstrip("m")))
                continue
        return size

    def vx_get_lv_disks(self, devname):
        """
         dg vg_sanperftest all        all      27000    1426245297.43.parcl1110221a
         dm 28785_281    3pardata0_281 auto    32.00m   34782.68m -
         sd 28785_281-01 lvset_sanperftest_01-01 28785_281 0.00m 16384.00m 0.00m 3pardata0_281 ENA
         sd 28785_281-02 lvset_sanperftest_02-01 28785_281 16384.00m 10240.00m 0.00m 3pardata0_281 ENA
         sd 28785_281-03 lv_sanperftest_01-01 28785_281 26624.00m 1024.00m 0.00m 3pardata0_281 ENA
         pl lv_sanperftest_01-01 lv_sanperftest_01 ENABLED ACTIVE 1024.00m CONCAT - RW
         pl lvset_sanperftest_01-01 lvset_sanperftest_01 ENABLED ACTIVE 16384.00m CONCAT - RW
         pl lvset_sanperftest_02-01 lvset_sanperftest_02 ENABLED ACTIVE 10240.00m CONCAT - RW
         v  lv_sanperftest_01 -       ENABLED  ACTIVE   1024.00m SELECT    -        fsgen
         v  lvset_sanperftest_01 vset_sanperftest ENABLED ACTIVE 16384.00m SELECT - fsgen
         v  lvset_sanperftest_02 vset_sanperftest ENABLED ACTIVE 10240.00m SELECT - fsgen
         vt vset_sanperftest -        ENABLED  ACTIVE   2
        """
        _dg, _vt = devname.split("/")
        out = self.vxprint(_dg)
        sd = {}
        v = []
        pl = []
        lines = out.split("\n")
        lines.reverse()
        for line in lines:
            l = line.split()
            if len(l) < 5:
                continue
            if l[0] == "v":
                name = l[1]
                if l[2] == _vt or l[1] == _vt:
                    v.append(name)
                continue
            if l[0] == "pl":
                name = l[1]
                if l[2] in v:
                    pl.append(name)
                continue
            if l[0] == "sd":
                name = l[1]
                if l[2] in pl:
                    dm = l[3]
                    size = int(float(l[5].rstrip("m")))
                    if dm not in sd:
                        sd[dm] = {
                          "devname": dm,
                          "size": size
                        }
                    else:
                        sd[dm]["size"] += size
            if l[0] == "dm":
               dmname = l[1]
               dname = l[2]
               for dm in sd:
                   if sd[dm]["devname"] == dmname:
                       sd[dm]["devname"] = dname
        return sd.values()

    def vxprint(self, dg):
        if dg in self.vxprint_cache:
            return self.vxprint_cache[dg]
        cmd = ["vxprint", "-t", "-u", "m", "-g", dg]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        self.vxprint_cache[dg] = out
        return out

    def get_mp_dmp(self):
        self.dmp = {}
        if not which("vxdmpadm"):
            return {}
        cmd = ['vxdmpadm', 'getsubpaths']
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            return {}
        lines = out.split('\n')
        if len(lines) < 3:
            return {}
        lines = lines[2:]
        mp_h = {}
        for line in lines:
            l = line.split()
            if len(l) < 4:
                continue
            name = l[3]
            dev = self.devprefix+l[0]
            if name in self.dmp:
                self.dmp[name].append(dev)
            else:
                self.dmp[name] = [dev]
            if name not in mp_h or mp_h[name] == "unknown" or mp_h[name] == name:
                d = self.vxdisk_cache.get("/dev/vx/rdmp/"+name)
                if d is None:
                    wwid = name
                else:
                    wwid = d.get("wwid")
                mp_h[name] = wwid
        return mp_h

    def vx_vid(self, dev):
        self.load_vxdisk_cache()
        if dev in self.vxdisk_cache:
            return self.vxdisk_cache[dev].get("vid", "")
        return ""

    def vx_pid(self, dev):
        self.load_vxdisk_cache()
        if dev in self.vxdisk_cache:
            return self.vxdisk_cache[dev].get("pid", "")
        return ""

    def vx_inq(self, dev):
        self.load_vxdisk_cache()
        if dev in self.vxdisk_cache:
            return self.vxdisk_cache[dev].get("wwid", "unknown")
        return "unknown"

    def load_vxdisk_cache(self):
        if len(self.vxdisk_cache) != 0:
            return
        cmd = ["/usr/sbin/vxdisk", "-p", "list"]
        (out, err, ret) = justcall(cmd)
        if ret != 0:
            return "unknown"
        for line in out.split("\n"):
            l = line.split(": ")
            if len(l) != 2:
                continue
            key = l[0].strip()
            if key == "DISK":
                disk = l[1].strip()
                _key = "/dev/vx/rdmp/"+disk
                self.vxdisk_cache[_key] = {"wwid": disk}
            elif key == "SCSI3_VPD_ID":
                # NAA:6000... or 6000...
                self.vxdisk_cache[_key]["wwid"] = l[1].split(":")[-1].strip()
            elif key == "LUN_SIZE":
                self.vxdisk_cache[_key]["size"] = int(l[1].strip())/2048
            elif key == "DMP_SINGLE_PATH":
                self.vxdisk_cache[_key]["devpath"] = l[1].strip()
            elif key == "PID":
                self.vxdisk_cache[_key]["pid"] = l[1].replace("-SUN", "").strip()
            elif key == "VID":
                self.vxdisk_cache[_key]["vid"] = l[1].strip()

    def load_vx_dmp(self):
        self.load_vxdisk_cache()
        if os.path.exists("/dev/rdsk"):
            self.devprefix = "/dev/rdsk/"
        else:
            self.devprefix = "/dev/"
        wwid_h = self.get_mp_dmp()
        for devname in wwid_h:
            rdevpath = "/dev/vx/rdmp/"+devname
            if rdevpath not in self.vxdisk_cache:
                continue
            size = self.vxdisk_cache[rdevpath].get("size", 0)
            d = self.add_dev(devname, size, "multipath")
            if d is None:
                continue
            d.set_devpath("/dev/vx/dmp/"+devname)
            d.set_devpath(rdevpath)
            d.set_alias(wwid_h[devname])

            for path in self.dmp[devname]:
                pathdev = path.replace(self.devprefix, "")
                p = self.add_dev(pathdev, size, "linear")
                p.set_devpath(path)
                p.add_child(devname)
                d.add_parent(pathdev)
                if False and self.devprefix == "/dev/rdsk/" and path.endswith("s2"):
                    _pathdev = pathdev[:-2]
                    p = self.add_dev(_pathdev, size, "linear")
                    p.add_child(devname)
                    d.add_parent(_pathdev)

    def load_vx_vm(self):
        for devpath in glob.glob("/dev/vx/dsk/*/*"):
            devname = devpath.replace("/dev/vx/dsk/", "")
            disks = self.vx_get_lv_disks(devname)
            if len(disks) == 0:
                # discard snaps for now
                continue
            size = self.vx_get_size(devname)
            d = self.add_dev(devname, size, "linear")
            if d is None:
                continue
            d.set_devpath("/dev/vx/dsk/"+devname)
            d.set_devpath("/dev/vx/rdsk/"+devname)

            for disk in disks:
                cdevname = disk["devname"]
                csize = disk["size"]
                p = self.add_dev(cdevname, csize, "linear")
                p.set_devpath(cdevname)
                p.add_child(devname, csize, "linear")
                d.add_parent(cdevname, csize, "linear")


   0707010005c929000081b40000000000000000000000015ab217540000026b000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/rcDevTreeWindows.py    import rcDevTree
import wmi

class DevTree(rcDevTree.DevTree):

    def load_diskdrive(self):
        if not hasattr(self, 'wmi'):
            self.wmi = wmi.WMI()
        for drive in self.wmi.WIN32_DiskDrive():
            d = self.add_dev(drive.DeviceId, int(drive.size)//1024, "linear")
            d.set_devpath(drive.DeviceId)

    def load(self, di=None):
        self.load_diskdrive()

    def blacklist(self, devname):
        return False

if __name__ == "__main__":
    tree = DevTree()
    tree.load()
    #print(tree)
    tree.print_tree_bottom_up()
    #print(map(lambda x: x.alias, tree.get_top_devs()))
 0707010005c92a000081b40000000000000000000000015ab217540000029d000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/rcDiskInfo.py  import rcExceptions as ex

class diskInfo(object):
    """Parent class for diskInfo OS"""

    print_diskinfo_fmt = "%-12s %-8s %12s MB %-8s %-8s %-16s"

    def disk_id(self, dev):
        return "tbd"

    def disk_vendor(self, dev):
        return "tbd"

    def disk_model(self, dev):
        return "tbd"

    def disk_size(self, dev):
        return "tbd"

    def print_diskinfo_header(self):
        print(self.print_diskinfo_fmt%(
          "hbtl",
          "devname",
          "size",
          "dev",
          "vendor",
          "model",
        ))

    def scanscsi(self, hba=None, target=None, lun=None):
        raise ex.excError("not implemented")


   0707010005c92b000081b40000000000000000000000015ab2175400000832000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/rcDiskInfoAIX.py   from rcUtilities import call, justcall
import rcDiskInfo
import re

class diskInfo(rcDiskInfo.diskInfo):

    def __init__(self):
        self.h = {}

    def scan(self, lname):
        vid = 'unknown'
        pid = 'unknown'
        wwid = 'unknown'
        size = 'unknown'

        cmd = ['lscfg', '-vpl', lname]
        (ret, out, err) = call(cmd)

        for f in out.split('\n'):
            if "Manufacturer" in f:
                vid = f.split('.')[-1]
            if "Machine Type and Model" in f:
                pid = f.split('.')[-1]

        cmd = ['bootinfo', '-s', lname]
        out, err, ret = justcall(cmd)
        if ret == 0:
            size = int(out.strip())
        else:
            size = 0

        wwid = self.odmget(lname, 'ww_name').replace('0x', '')
        if wwid == 'unknown':
            wwid = self.get_vscsi_id(lname)

        self.h[lname] = dict(vid=vid, pid=pid, wwid=wwid, size=size)

    def get_vscsi_id(self, lname):
        cmd = ['lscfg', '-l', lname]
        (ret, out, err) = call(cmd)
        if ret != 0:
            return 'unknown'
        l = out.split()
        if len(l) < 2:
            return 'unknown'
        d = l[1]
        regex = re.compile(r'-C[0-9]+-T[0-9]+')
        d = regex.sub('', d)
        return d

    def odmget(self, lname, attr):
        cmd = ['odmget', '-q', 'name='+lname+' AND attribute='+attr, 'CuAt']
        (ret, out, err) = call(cmd)
        for f in out.split('\n'):
            if "value" not in f:
                continue
            return f.split(" = ")[-1].strip('"')
        return 'unknown'


    def devkey(self, dev):
        dev = dev.replace("/dev/", "")
        return dev

    def get(self, dev, type):
        dev = self.devkey(dev)
        if dev not in self.h:
            self.scan(dev)
        return self.h[dev][type]

    def disk_id(self, dev):
        return self.get(dev, 'wwid')

    def disk_vendor(self, dev):
        return self.get(dev, 'vid')

    def disk_model(self, dev):
        return self.get(dev, 'pid')

    def disk_size(self, dev):
        return self.get(dev, 'size')

  0707010005c933000081b40000000000000000000000015ab2175400008107000000660000000000000000000000000000002700000000root/usr/share/opensvc/lib/rcDocker.py    # -*- coding: utf8 -*-

"""
The module implementing the DockerLib class.
"""
import os
import errno
from distutils.version import LooseVersion as V

import json
import re
import rcStatus
import rcExceptions as ex

from rcUtilities import which, justcall, lazy, unset_lazy
from rcGlobalEnv import rcEnv

os.environ['LANG'] = 'C'

class DockerLib(object):
    """
    Instanciated as the 'dockerlib' Svc lazy attribute, this class abstracts
    docker daemon ops.
    """
    def __init__(self, svc=None):
        self.svc = svc
        self.max_wait_for_dockerd = 5
        self.docker_info_done = False

        try:
            self.docker_data_dir = \
                self.svc.conf_get('DEFAULT', 'docker_data_dir')
        except ex.OptNotFound as exc:
            self.docker_data_dir = exc.default

        try:
            self.docker_daemon_private = \
                self.svc.conf_get('DEFAULT', 'docker_daemon_private')
        except ex.OptNotFound:
            if self.docker_data_dir:
                self.docker_daemon_private = True
            else:
                self.docker_daemon_private = False
        if rcEnv.sysname != "Linux":
            self.docker_daemon_private = False

        try:
            self.docker_exe_init = \
                self.svc.conf_get('DEFAULT', 'docker_exe')
        except ex.OptNotFound as exc:
            self.docker_exe_init = exc.default

        try:
            self.dockerd_exe_init = \
                self.svc.conf_get('DEFAULT', 'dockerd_exe')
        except ex.OptNotFound as exc:
            self.dockerd_exe_init = exc.default

        try:
            self.docker_daemon_args = \
                self.svc.conf_get('DEFAULT', 'docker_daemon_args')
        except ex.OptNotFound as exc:
            self.docker_daemon_args = exc.default

        try:
            self.docker_swarm_args = \
                self.svc.conf_get('DEFAULT', 'docker_swarm_args')
        except ex.OptNotFound as exc:
            self.docker_swarm_args = exc.default

        try:
            self.docker_swarm_managers = \
                self.svc.conf_get('DEFAULT', 'docker_swarm_managers')
        except ex.OptNotFound as exc:
            self.docker_swarm_managers = exc.default

        if self.docker_data_dir:
            if "--exec-opt" not in self.docker_daemon_args and self.docker_min_version("1.7"):
                self.docker_daemon_args += ["--exec-opt", "native.cgroupdriver=cgroupfs"]

        if "--token" in self.docker_swarm_args:
            raise ex.excError("--token must not be specified in DEFAULT.docker_swarm_args")

        self.docker_var_d = self.svc.var_d

        if self.docker_daemon_private:
            self.docker_socket = "unix://"+os.path.join(self.docker_var_d, 'docker.sock')
        else:
            self.docker_socket = None

        if self.docker_daemon_private:
            self.docker_pid_file = os.path.join(self.docker_var_d, 'docker.pid')
        else:
            self.docker_pid_file = None
            lines = [line for line in self.docker_info.splitlines() if "Root Dir" in line]
            try:
                self.docker_data_dir = lines[0].split(":")[-1].strip()
            except IndexError:
                self.docker_data_dir = None

        try:
            self.docker_cmd = [self.docker_exe]
            if self.docker_socket:
                self.docker_cmd += ['-H', self.docker_socket]
        except:
            self.docker_cmd = None

    def get_ps(self, refresh=False):
        """
        Return the 'docker ps' output from cache or from the command
        execution depending on <refresh>.
        """
        if refresh:
            unset_lazy(self, "docker_ps")
        return self.docker_ps

    @lazy
    def docker_ps(self):
        """
        The "docker ps" output.
        """
        cmd = self.docker_cmd + ['ps', '-a', '--no-trunc']
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError(err)
        return out

    def docker_node_rm(self, ref):
        """
        Execute "docker node rm <ref>"
        """
        cmd = self.docker_cmd + ['node', 'rm', ref]
        self.svc.log.debug("remove replaced node %s" % ref)
        self.svc.log.debug(" ".join(cmd))
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError(err)

    @lazy
    def docker_service_ls(self):
        """
        The "docker service ls" output.
        """
        cmd = self.docker_cmd + ['service', 'ls', "--format", "{{.ID}}::{{.Name}}::{{.Mode}}::{{.Replicas}}::{{.Image}}"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError(err)
        return out

    def docker_service_ps(self, service):
        """
        The "docker service ps <service>" output.
        """
        if service is None:
            return ""
        cmd = self.docker_cmd + ['service', 'ps', service]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError(err)
        return out

    def service_ps_data(self, service):
        lines = self.docker_service_ps(service).splitlines()
        if len(lines) < 2:
            return []
        ids = []
        for line in lines[1:]:
            if "\_" in line:
                # don't care about "history" lines
                continue
            line = line.strip().split()
            if len(line) == 0:
                continue
            ids.append(line[0])
        data = self.docker_inspect(ids)

        # discard lines with left nodes
        node_ids = self.node_ids()
        data = [inst for inst in data if "NodeID" in inst and inst["NodeID"] in node_ids]
        return data

    def docker_node_ls(self):
        """
        The "docker node ls" output.
        """
        cmd = self.docker_cmd + ['node', 'ls', '-q']
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError(err)
        return out

    def node_ids(self):
        return self.docker_node_ls().strip().splitlines()

    def node_ls_data(self):
        cmd = self.docker_cmd + ['node', 'inspect'] + self.node_ids()
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError(err)
        return json.loads(out)

    def node_data(self):
        cmd = self.docker_cmd + ['node', 'inspect', rcEnv.nodename]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError(err)
        return json.loads(out)[0]

    @lazy
    def service_ls_data(self):
        """
        A hash of services data as found in "docker service ls",
        indexed by service name.
        """
        data = {}
        for line in self.docker_service_ls.splitlines():
            line = line.strip().split("::")
            service_name = line[1].strip()
            data[service_name] = {
                "name": service_name,
                "id": line[0].strip(),
                "mode": line[2].strip(),
                "replicas": line[3].strip().split("/"),
                "image": line[4].strip(),
            }
        return data

    @lazy
    def container_id_by_name(self):
        """
        A hash of instances data as found in "docker ps", indexed by
        instance id.
        """
        lines = self.docker_ps.splitlines()
        if len(lines) < 2:
            return
        try:
            start = lines[0].index('NAMES')
        except (IndexError, ValueError):
            return
        data = {}
        for line in lines[1:]:
            if len(line.strip()) == 0:
                continue
            try:
                names = line[start:].strip().split(',')
            except IndexError:
                continue
            for name in names:
                # swarm names are preffixed by <nodename>/
                elements = name.split("/")
                container_name = elements[-1]
                if len(elements) == 2:
                    swarm_node = elements[0]
                else:
                    swarm_node = None
                data[container_name] = {
                    "id": line.split()[0],
                    "swarm_node": swarm_node,
                }
        return data

    def get_container_id_by_name(self, resource, refresh=False):
        """
        Return the container id for the <resource> container resource.
        Lookup in docker ps by docker name <svcname>.container.<n> where
        <n> is the identifier part of the resource id.
        """
        if refresh:
            unset_lazy(self, "docker_ps")
            unset_lazy(self, "container_id_by_name")
        if resource.docker_service:
            prefix = resource.service_name+"."
            if self.container_id_by_name is None:
                return
            running = self.get_running_instance_ids()
            for container_name, data in self.container_id_by_name.items():
                if data["id"] not in running:
                    continue
                if container_name.startswith(prefix):
                    return data["id"]
        else:
            if self.container_id_by_name is None or \
                resource.container_name not in self.container_id_by_name:
                return
            data = self.container_id_by_name[resource.container_name]
            return data["id"]

    def get_service_id_by_name(self, resource, refresh=False):
        """
        Return the service id for the <resource> container resource.
        Lookup in docker service ls by docker name <svcname>_container_<n>
        where <n> is the identifier part of the resource id.
        """
        if refresh:
            unset_lazy(self, "docker_service_ls")
            unset_lazy(self, "service_ls_data")
        if self.service_ls_data is None or \
           resource.service_name not in self.service_ls_data:
            return
        data = self.service_ls_data[resource.service_name]
        return data["id"]


    @lazy
    def docker_info(self):
        """
        The output of "docker info".
        """
        try:
            self.docker_exe
        except ex.excInitError:
            return ""
        cmd = [self.docker_exe, "info"]
        return justcall(cmd)[0]

    @lazy
    def docker_version(self):
        """
        The docker version.
        """
        try:
            cmd = [self.docker_exe, "--version"]
        except ex.excInitError:
            return "0"
        out = justcall(cmd)[0]
        elements = out.split()
        if len(elements) < 3:
            return False
        return elements[2].rstrip(",")

    def docker_min_version(self, version):
        """
        Return True if the docker version is at least <version>.
        """
        try:
            cmd = [self.docker_exe, "--version"]
        except ex.excInitError:
            return False
        if V(self.docker_version) >= V(version):
            return True
        return False

    def get_running_service_ids(self, refresh=False):
        """
        Return the list of running docker services id.
        """
        if refresh:
            unset_lazy(self, "running_service_ids")
            unset_lazy(self, "docker_service_ls")
            unset_lazy(self, "service_ls_data")
        return self.running_service_ids

    @lazy
    def running_service_ids(self):
        """
        The list of running docker services id.
        """
        if self.service_ls_data is None:
            return []
        return [service["id"] for service in self.service_ls_data.values()]

    def get_running_instance_ids(self, refresh=False):
        """
        Return the list of running docker instances id.
        """
        if refresh:
            unset_lazy(self, "running_instance_ids")
        return self.running_instance_ids

    @lazy
    def running_instance_ids(self):
        """
        The list of running docker instances id.
        """
        cmd = self.docker_cmd + ['ps', '-q', '--no-trunc']
        out = justcall(cmd)[0]
        return out.replace('\n', ' ').split()

    def get_run_image_id(self, resource, run_image=None):
        """
        Return the full docker image id
        """
        if run_image is None and hasattr(resource, "run_image"):
            run_image = resource.run_image
        if len(run_image) == 12 and re.match('^[a-f0-9]*$', run_image):
            return run_image
        if run_image.startswith("sha256:"):
            return run_image

        try:
            image_name, image_tag = run_image.split(':')
        except ValueError:
            return

        if self.docker_min_version("1.13"):
            data = self.docker_image_inspect(run_image)
            if data is None:
                self.docker_pull(run_image)
                data = self.docker_image_inspect(run_image)
            if data is None:
                raise ValueError("image %s not pullable" % run_image)
            return data["Id"]

        cmd = self.docker_cmd + ['images', '--no-trunc', image_name]
        results = justcall(cmd)
        if results[2] != 0:
            return run_image
        for line in results[0].splitlines():
            elements = line.split()
            if len(elements) < 3:
                continue
            if elements[1] == image_tag:
                return elements[2]
        return run_image

    def docker_pull(self, ref):
        self.svc.log.info("pulling docker image %s" % ref)
        cmd = self.docker_cmd + ['pull', ref]
        results = justcall(cmd)
        if results[2] != 0:
            raise ex.excError(results[1])

    @lazy
    def images(self):
        """
        The hash of docker images, indexed by image id.
        """
        if self.docker_cmd is None:
            return {}
        cmd = self.docker_cmd + ['images', '--no-trunc']
        results = justcall(cmd)
        if results[2] != 0:
            return
        data = {}
        for line in results[0].splitlines():
            elements = line.split()
            if len(elements) < 3:
                continue
            if elements[2] == "IMAGE":
                continue
            data[elements[2]] = elements[0]+':'+elements[1]
        return data

    def _info(self):
        """
        Return the keys contributed to resinfo.
        """
        if self.docker_info_done:
            return []
        data = []
        data += self._docker_info_version()
        data += self._docker_info_drivers()
        data += self._docker_info_images()
        return data

    def _docker_info_version(self):
        """
        Return the docker version key conttributed to resinfo.
        """
        return [[
            "",
            "docker_version",
            self.docker_version
        ]]

    def _docker_info_drivers(self):
        """
        Return the docker drivers keys conttributed to resinfo.
        """
        data = []
        lines = self.docker_info.splitlines()
        for line in lines:
            elements = line.split(": ")
            if len(elements) < 2:
                continue
            if elements[0] == "Storage Driver":
                data.append(["", "storage_driver", elements[1]])
            if elements[0] == "Execution Driver":
                data.append(["", "exec_driver", elements[1]])
        return data

    def _docker_info_images(self):
        """
        Return the per-container resource resinfo keys.
        """
        data = []
        images_done = []

        # referenced images
        for resource in self.svc.get_resources("container.docker"):
            image_id = self.get_run_image_id(resource)
            images_done.append(image_id)
            data.append([resource.rid, "run_image", resource.run_image])
            data.append([resource.rid, "docker_image_id", image_id])
            data.append([resource.rid, "docker_instance_id", resource.container_id])

        # unreferenced images
        for image_id in self.images:
            if image_id in images_done:
                continue
            data.append(["", "docker_image_id", image_id])
        self.docker_info_done = True

        return data

    def image_userfriendly_name(self, resource):
        """
        Return the container resource docker image name if possible,
        else return the image id.
        """
        if ':' in resource.run_image:
            return resource.run_image
        if self.images is None:
            return resource.run_image
        if resource.run_image in self.images:
            return self.images[resource.run_image]
        return resource.run_image

    def docker_inspect(self, container_id):
        """
        Return the "docker inspect" data dict.
        """
        try:
            self.docker_exe
        except ex.excInitError:
            return {}
        if container_id is None:
            raise IndexError("container id is None")
        elif isinstance(container_id, list):
            cmd = self.docker_cmd + ['inspect'] + container_id
            out = justcall(cmd)[0]
            data = json.loads(out)
            return data
        else:
            cmd = self.docker_cmd + ['inspect', container_id]
            out = justcall(cmd)[0]
            data = json.loads(out)
            return data[0]

    def docker_volume_inspect(self, vol_id):
        """
        Return the "docker volume inspect" data dict.
        """
        try:
            self.docker_exe
        except ex.excInitError:
            return {}
        if vol_id is None:
            raise IndexError("vol id is None")
        elif isinstance(vol_id, list):
            cmd = self.docker_cmd + ["volume", "inspect"] + vol_id
            out = justcall(cmd)[0]
            data = json.loads(out)
            return data
        else:
            cmd = self.docker_cmd + ["volume", "inspect", vol_id]
            out = justcall(cmd)[0]
            data = json.loads(out)
            return data[0]

    def docker_service_inspect(self, service_id):
        """
        Return the "docker service inspect" data dict.
        """
        cmd = self.docker_cmd + ['service', 'inspect', service_id]
        out = justcall(cmd)[0]
        data = json.loads(out)
        return data[0]

    def docker_image_inspect(self, image_id):
        """
        Return the "docker image inspect" data dict.
        """
        cmd = self.docker_cmd + ['image', 'inspect', image_id]
        out = justcall(cmd)[0]
        data = json.loads(out)
        if len(data) == 0:
            return
        return data[0]

    def repotag_to_image_id(self, repotag):
        data = self.docker_image_inspect(repotag)
        if data is None:
            return
        return data["Id"]

    def docker_stop(self):
        """
        Stop the docker daemon if possible.
        """
        def can_stop():
            """
            Return True if the docker daemon can be stopped.
            """
            if not self.docker_daemon_private:
                return False
            if not self.docker_running():
                return False
            if self.docker_data_dir is None:
                return False
            if not os.path.exists(self.docker_pid_file):
                return False
            if len(self.get_running_instance_ids(refresh=True)) > 0:
                return False
            return True

        if not can_stop():
            return

        try:
            with open(self.docker_pid_file, 'r') as ofile:
                pid = int(ofile.read())
        except (OSError, IOError):
            self.svc.log.warning("can't read %s. skip docker daemon kill",
                                 self.docker_pid_file)
            return

        self.svc.log.info("no more container handled by docker daemon (pid %d)."
                          " shut it down", pid)
        import signal
        import time
        tries = 15
        os.kill(pid, signal.SIGTERM)
        unset_lazy(self, "docker_info")

        while self.docker_running() and tries > 0:
            unset_lazy(self, "docker_info")
            tries -= 1
            time.sleep(1)
        if self.docker_running() and tries == 0:
            self.svc.log.warning("dockerd did not stop properly. send a kill "
                                 "signal")
            try:
                os.kill(pid, signal.SIGKILL)
            except OSError as exc:
                if exc.errno == errno.ESRCH:
                    # already dead
                    pass
                else:
                    raise ex.excError("failed to kill docker daemon: %s" % str(exc))

    @lazy
    def dockerd_cmd(self):
        """
        The docker daemon startup command, adapted to the docker version.
        """
        if self.docker_cmd is None:
            return []
        if self.docker_min_version("17.05"):
            cmd = [
                self.dockerd_exe,
                '-H', self.docker_socket,
                '--data-root', self.docker_data_dir,
                '-p', self.docker_pid_file
            ]
        elif self.docker_min_version("1.13"):
            cmd = [
                self.dockerd_exe,
                '-H', self.docker_socket,
                '-g', self.docker_data_dir,
                '-p', self.docker_pid_file
            ]
        elif self.docker_min_version("1.8"):
            cmd = [
                self.docker_exe, 'daemon',
                '-H', self.docker_socket,
                '-g', self.docker_data_dir,
                '-p', self.docker_pid_file
            ]
        else:
            cmd = self.docker_cmd + [
                '-r=false', '-d',
                '-g', self.docker_data_dir,
                '-p', self.docker_pid_file
            ]
        if self.docker_min_version("1.9") and '--exec-root' not in str(self.docker_daemon_args):
            cmd += ["--exec-root", os.path.join(self.svc.var_d, "docker_exec")]
        cmd += self.docker_daemon_args
        return cmd

    def _docker_data_dir_resource(self):
        """
        Return the service fs resource handling the docker data dir, or
        None if any.
        """
        mntpts = []
        mntpt_res = {}
        for resource in self.svc.get_resources('fs'):
            mntpts.append(resource.mount_point)
            mntpt_res[resource.mount_point] = resource
        for mntpt in sorted(mntpts, reverse=True):
            if mntpt.startswith(self.docker_data_dir):
                return mntpt_res[mntpt]

    def docker_start(self, verbose=True):
        """
        Start the docker daemon if in private mode and not already running.
        """
        if not self.docker_daemon_private:
            return
        if self.docker_cmd is None:
            raise ex.excError("docker executable not found")
        import lock
        lockfile = os.path.join(rcEnv.paths.pathlock, self.svc.svcname+'.docker_start')
        try:
            lockfd = lock.lock(timeout=15, delay=1, lockfile=lockfile)
        except lock.LOCK_EXCEPTIONS as exc:
            self.svc.log.error("dockerd start lock acquire failed: %s",
                               str(exc))
            return

        # Sanity checks before deciding to start the daemon
        if self.docker_running():
            lock.unlock(lockfd)
            return

        if self.docker_data_dir is None:
            lock.unlock(lockfd)
            return

        resource = self._docker_data_dir_resource()
        if resource is not None:
            state = resource._status()
            if state not in (rcStatus.UP, rcStatus.STDBY_UP):
                self.svc.log.warning("the docker daemon data dir is handled by the %s "
                                     "resource in %s state. can't start the docker "
                                     "daemon", resource.rid, rcStatus.Status(state))
                lock.unlock(lockfd)
                return

        if os.path.exists(self.docker_pid_file):
            self.svc.log.warning("removing leftover pid file %s", self.docker_pid_file)
            os.unlink(self.docker_pid_file)

        # Now we can start the daemon, creating its data dir if necessary
        cmd = self.dockerd_cmd

        if verbose:
            self.svc.log.info("starting docker daemon")
            self.svc.log.info(" ".join(cmd))
        import subprocess
        subprocess.Popen(
            ['nohup'] + cmd,
            stdout=open('/dev/null', 'w'),
            stderr=open('/dev/null', 'a'),
            preexec_fn=os.setpgrp,
            close_fds=True,
        )

        import time
        try:
            for _ in range(self.max_wait_for_dockerd):
                if self._docker_working():
                    return
                time.sleep(1)
        finally:
            lock.unlock(lockfd)

        unset_lazy(self, "docker_ps")
        unset_lazy(self, "docker_service_ls")
        unset_lazy(self, "service_ls_data")
        unset_lazy(self, "container_id_by_name")
        unset_lazy(self, "docker_info")
        unset_lazy(self, "running_service_ids")
        unset_lazy(self, "running_instance_ids")
        unset_lazy(self, "images")

    def docker_running(self):
        """
        Return True if the docker daemon is running.
        """
        if self.docker_daemon_private:
            return self._docker_running_private()
        else:
            return self._docker_running_shared()

    def _docker_running_shared(self):
        """
        Return True if the docker daemon is running.
        """
        if self.docker_info == "":
            return False
        return True

    def _docker_running_private(self):
        """
        Return True if the docker daemon is running.
        """
        if not os.path.exists(self.docker_pid_file):
            self.svc.log.debug("docker_running: no pid file %s", self.docker_pid_file)
            return False
        try:
            with open(self.docker_pid_file, "r") as ofile:
                buff = ofile.read()
        except IOError as exc:
            if exc.errno == errno.ENOENT:
                return False
            return ex.excError("docker_running: "+str(exc))
        self.svc.log.debug("docker_running: pid found in pid file %s", buff)
        exe = os.path.join(os.sep, "proc", buff, "exe")
        try:
            exe = os.path.realpath(exe)
        except OSError:
            self.svc.log.debug("docker_running: no proc info in /proc/%s", buff)
            try:
                os.unlink(self.docker_pid_file)
            except OSError:
                pass
            return False
        if "docker" not in exe:
            self.svc.log.debug("docker_running: pid found but owned by a "
                               "process that is not a docker (%s)", exe)
            try:
                os.unlink(self.docker_pid_file)
            except OSError:
                pass
            return False
        return True

    def _docker_working(self):
        """
        Return True if the docker daemon responds to a simple 'info' request.
        """
        cmd = self.docker_cmd + ['info']
        ret = justcall(cmd)[2]
        if ret != 0:
            return False
        return True

    @lazy
    def docker_exe(self):
        """
        Return the docker executable to use, using the service configuration
        docker_exe as the first choice, and a docker.io or docker exe found
        in PATH as a fallback.
        """
        if self.docker_exe_init and which(self.docker_exe_init):
            return self.docker_exe_init
        elif which("docker.io"):
            return "docker.io"
        elif which("docker"):
            return "docker"
        else:
            raise ex.excInitError("docker executable not found")

    @lazy
    def dockerd_exe(self):
        if self.dockerd_exe_init and which(self.dockerd_exe_init):
            return self.dockerd_exe_init
        elif which("dockerd"):
            return "dockerd"
        else:
            raise ex.excInitError("dockerd executable not found")

    def join_token(self, ttype):
        self.docker_start()
        cmd = self.docker_cmd + ["swarm", "join-token", ttype]
        results = justcall(cmd)
        if results[2] != 0:
            raise ex.excError(results[1])
        token = None
        for line in results[0].splitlines():
            line = line.split()
            if "--token" not in line:
                continue
            token = line[line.index("--token")+1]
            addr = line[-1]
            return {"token": token, "addr": addr}
        raise ex.excError("unable to determine the swarm worker join token")

    def swarm_initialized(self):
        if self.swarm_node_role == "none":
            return False
        return True

    def join_token_dump_file(self, ttype):
        return os.path.join(self.svc.var_d, "swarm_" + ttype + "_join_token")

    def dump_join_tokens(self):
        for ttype in ("manager", "worker"):
            with open(self.join_token_dump_file(ttype), "w") as fp:
                fp.write(json.dumps(self.join_token(ttype)))

    def load_join_token(self, ttype):
        fpath = self.join_token_dump_file(ttype)
        if not os.path.exists(fpath):
            raise ex.excError("the join token has not been transfered by the flex primary node")
        with open(fpath, "r") as fp:
            data = json.load(fp)
        return data

    @lazy
    def files_to_sync(self):
        fpaths = []
        self.dump_join_tokens()
        for ttype in ("manager", "worker"):
            fpath = self.join_token_dump_file(ttype)
            if os.path.exists(fpath):
                fpaths.append(fpath)
        return fpaths

    def init_swarm(self):
        if self.swarm_initialized():
            return
        if rcEnv.nodename == self.svc.flex_primary:
            self.init_swarm_leader()
        elif rcEnv.nodename in self.docker_swarm_managers:
            self.init_swarm_manager()
        else:
            self.init_swarm_worker()
        unset_lazy(self, "swarm_node_role")

    def init_swarm_leader(self):
        cmd = self.docker_cmd + ['swarm', 'init']
        if len(self.docker_swarm_args) > 0:
            cmd += self.docker_swarm_args
        self.svc.log.info(" ".join(cmd))
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError(err)

    def init_swarm_manager(self):
        data = self.load_join_token("manager")
        cmd = self.docker_cmd + ['swarm', 'join', '--token', data["token"], data["addr"]]
        if len(self.docker_swarm_args) > 0:
            cmd += self.docker_swarm_args
        self.svc.log.info(" ".join(cmd))
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError(err)

    def init_swarm_worker(self):
        data = self.load_join_token("worker")
        cmd = self.docker_cmd + ['swarm', 'join', '--token', data["token"], data["addr"]]
        if len(self.docker_swarm_args) > 0:
            cmd += self.docker_swarm_args
        self.svc.log.info(" ".join(cmd))
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError(err)

    def docker_swarm_leave(self):
        if self.swarm_node_role == "none":
            return
        cmd = self.docker_cmd + ['swarm', 'leave']
        ret, out, err = self.svc.vcall(cmd)
        if ret != 0:
            raise ex.excError(err)
        unset_lazy(self, "swarm_node_role")

    @lazy
    def swarm_node_role(self):
        """
        Return
        * none : no role in the swarm, not joined yet
        * worker
        * leader
        * reachable
        """
        if not self.docker_running():
            return "none"
        cmd = self.docker_cmd + ['node', 'ls']
        out, err, ret = justcall(cmd)
        if ret != 0:
            if "docker swarm" in err:
                return "none"
            else:
                return "worker"
        for line in out.splitlines():
            line = line.replace(" * ", " ")
            line = line.strip().split()
            if len(line) < 4:
                continue
            if line[1] != rcEnv.nodename:
                continue
            if line[-1] in ("Leader", "Reachable"):
                return line[-1].lower()
            else:
                return "unknown"

    def nodes_purge(self):
        """
        Remove lingering nodes, ie those in down state and
        with an active instance matching the hostname.
        """
        if self.swarm_node_role != "leader":
            return
        nodes = self.node_ls_data()
        down = {}
        for node in nodes:
            nodename = node["Description"]["Hostname"]
            if node["Status"]["State"] != "down":
                continue
            if nodename not in down:
                down[nodename] = []
            down[nodename].append(node["ID"])
        for node in nodes:
            nodename = node["Description"]["Hostname"]
            if node["Status"]["State"] != "ready":
                continue
            if nodename not in down:
                continue
            for node_id in down[nodename]:
                self.docker_node_rm(node_id)
 0707010005c92c000081b40000000000000000000000015ab2175400000041000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/rcDiskInfoDarwin.py    import rcDiskInfo

class diskInfo(rcDiskInfo.diskInfo):
    pass
   0707010005c92d000081b40000000000000000000000015ab217540000021a000000660000000000000000000000000000003000000000root/usr/share/opensvc/lib/rcDiskInfoFreeBSD.py   from rcUtilities import call, which
import rcDiskInfo

class diskInfo(rcDiskInfo.diskInfo):
    disk_ids = {}

    def __init__(self, deferred=False):
        pass

    def disk_id(self, dev):
        print("%s:disk_id TODO"%__file__)
        return ""

    def disk_vendor(self, dev):
        print("%s:disk_vendor TODO"%__file__)
        return ""

    def disk_model(self, dev):
        print("%s:disk_model TODO"%__file__)
        return ""

    def disk_size(self, dev):
        print("%s:disk_size TODO"%__file__)
        return 0

  0707010005c92e000081b40000000000000000000000015ab2175400001a29000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/rcDiskInfoHP-UX.py from rcUtilities import justcall, which
import rcDiskInfo
import os

class diskInfo(rcDiskInfo.diskInfo):
    legacy_size_cache = {}
    legacy_wwid_cache = {}

    def __init__(self):
        self.load_cache()

    def load_cache(self):
        self.load_aliases()

        self.h = {}
        cmd = ["scsimgr", "-p", "get_attr", "all_lun", "-a", "wwid", "-a", "device_file", "-a", "vid", "-a", "pid", "-a", "capacity"]
        out, err, ret = justcall(cmd)
        for e in out.split('\n'):
            if len(e) == 0:
                continue
            (wwid, dev, vid, pid, size) = e.split(':')
            wwid = wwid.replace('0x', '')
            if len(size) != 0:
                size = int(size)/2048
            else:
                size = 0
            vid = vid.strip('" ')
            pid = pid.strip('" ')
            if dev in self.aliases:
                aliases = self.aliases[dev]
            else:
                aliases = [dev]
            for alias in aliases:
                self.h[alias] = dict(wwid=wwid, vid=vid, pid=pid, size=size)

    def load_ioscan(self, refresh=False):
        if hasattr(self, "ioscan") and not refresh:
            return self.ioscan
        cmd = ['/usr/sbin/ioscan', '-FunNC', 'disk']
        out, err, ret = justcall(cmd)
        if ret != 0:
            return
        self.ioscan = []
        """
        virtbus:wsio:T:T:F:1:13:10:disk:esdisk:64000/0xfa00/0xa:0 0 4 50 0 0 0 0 51 248 164 14 250 83 253 237 :18:root.ext_virtroot.esvroot.esdisk:esdisk:CLAIMED:DEVICE:EMC     SYMMETRIX:-1:online
                      /dev/disk/disk17            /dev/disk/disk17_p3         /dev/rdisk/disk17_p1
                      /dev/disk/disk17_p1         /dev/pt/x64lmwbieb9_system  /dev/rdisk/disk17_p2
                      /dev/disk/disk17_p2         /dev/rdisk/disk17           /dev/rdisk/disk17_p3
        """
        for line in out.split('\n'):
            if not line.startswith(' ') and not line.startswith('\t') and len(line) > 0:
                l = line.split(":")
                blk_major = l[5]
                raw_major = l[6]
                index = l[7]
                vendor = l[17]
                # mark ready for insertion as soon as we get a devname
                devname = None
            elif devname is None:
                devname = line.split()[0]
                self.ioscan.append({
                  'devname': devname,
                  'dev': ':'.join((blk_major, index)),
                  'rdev': ':'.join((raw_major, index)),
                  'vendor': vendor,
                })
        return self.ioscan

    def load_aliases(self):
        self.aliases = {}
        cmd = ['/usr/sbin/ioscan', '-FunNC', 'disk']
        out, err, ret = justcall(cmd)
        if ret != 0:
            return
        l = []
        for line in out.split('\n')+[':']:
            if ':' in line:
                if len(l) > 0:
                    for name in l:
                         self.aliases[name] = l
                l = []
                continue
            for w in line.split():
                l.append(w)

    def dev2char(self, dev):
        dev = dev.replace("/dev/disk/", "/dev/rdisk/")
        dev = dev.replace("/dev/dsk/", "/dev/rdsk/")
        return dev

    def scan(self, dev):
        cmd = ["scsimgr", "-p", "get_attr", "-D", self.dev2char(dev), "-a", "wwid", "-a", "device_file", "-a", "vid", "-a", "pid", "-a", "capacity"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            self.h[dev] = dict(wwid="", vid="", pid="", size=0)
            return
        (wwid, foo, vid, pid, size) = out.split(':')
        wwid = wwid.replace('0x', '')
        if len(size) != 0:
            size = int(size)/2048
        else:
            size = 0
        vid = vid.strip('" ')
        pid = pid.strip('" ')
        self.h[dev] = dict(wwid=wwid, vid=vid, pid=pid, size=size)

    def get(self, dev, type):
        if dev not in self.h:
            self.scan(dev)
        return self.h[dev][type]

    def disk_id(self, dev):
        id = self.get(dev, 'wwid')
        if len(id) == 0:
            id = self.get_legacy_wwid(dev)
        return id

    def disk_vendor(self, dev):
        return self.get(dev, 'vid')

    def disk_model(self, dev):
        return self.get(dev, 'pid')

    def disk_size(self, dev):
        size = self.get(dev, 'size')
        if size == 0:
            size = self.get_legacy_size(dev)
        if size is None or size == "":
            # broken disk
            size = 0
        return size

    def print_diskinfo(self, info):
        info['size'] = self.disk_size(info['devname'])
        info['hbtl'] = "#:#:#:#"
        print(self.print_diskinfo_fmt%(
          info['hbtl'],
          os.path.basename(info['devname']),
          info['size'],
          info['dev'],
          info['vendor'],
          '',
        ))

    def scanscsi(self, hba=None, target=None, lun=None):
        ioscan_before = self.load_ioscan()
        disks_before = map(lambda x: x['devname'], ioscan_before)

        cmd = ['/usr/sbin/ioscan', '-fnC', 'disk']
        out, err, ret = justcall(cmd)
        if ret != 0:
            return

        ioscan_after = self.load_ioscan(refresh=True)
        disks_after = map(lambda x: x['devname'], ioscan_after)
        new_disks = set(disks_after) - set(disks_before)

        self.print_diskinfo_header()
        for info in ioscan_after:
            if info['devname'] not in new_disks:
                continue
            self.print_diskinfo(info)

        return 0

    def get_legacy_wwid(self, devpath):
        if devpath in self.legacy_wwid_cache:
            self.legacy_wwid_cache[devpath]
        if which("autopath"):
            wwid = self.get_autopath_wwid(devpath)
            self.legacy_wwid_cache[devpath] = wwid
            return wwid
        return ""

    def get_autopath_wwid(self, devpath):
        cmd = ["autopath", "display", devpath]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return ""
        for line in out.split("\n"):
            if "Lun WWN" in line:
                return line.split(": ")[-1].replace("-","").lower()
        return ""

    def get_legacy_size(self, devpath):
        """ return devpath size in megabytes
        """
        if devpath in self.legacy_size_cache:
            return self.legacy_size_cache[devpath]
        if not which("diskinfo"):
            return 0
        cmd = ["diskinfo", "-b", devpath.replace("dsk", "rdsk").replace("disk", "rdisk")]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return 0
        size = int(out.strip())/1024
        self.legacy_size_cache[devpath] = size
        return size

   0707010005c92f000081b40000000000000000000000015ab2175400003169000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/rcDiskInfoLinux.py from __future__ import print_function
import sys
import os
import re
from rcUtilities import justcall, which
from rcUtilitiesLinux import udevadm_settle
import rcDiskInfo
import math
from rcGlobalEnv import rcEnv
import rcDevTreeVeritas
import glob

class diskInfo(rcDiskInfo.diskInfo):
    disk_ids = {}

    def __init__(self, deferred=False):
        pass

    def prefix_local(self, id):
        return '.'.join((rcEnv.nodename, id))

    def disk_id(self, dev):
        if 'cciss' in dev:
            id = self.cciss_id(dev)
        elif dev.startswith('/dev/disk/by-id/wwn-0x'):
            id = dev.replace('/dev/disk/by-id/wwn-0x', '')
        elif dev.startswith('/dev/disk/by-id/scsi-2'):
            id = dev.replace('/dev/disk/by-id/scsi-2', '')
        elif dev.startswith('/dev/disk/by-id/scsi-3'):
            id = dev.replace('/dev/disk/by-id/scsi-3', '')
        elif dev.startswith('/dev/mapper/3'):
            id = dev.replace('/dev/mapper/3', '')
        elif dev.startswith('/dev/mapper/2'):
            id = dev.replace('/dev/mapper/2', '')
        elif "dmp/" in dev:
            id = rcDevTreeVeritas.DevTreeVeritas().vx_inq(dev)
        elif "Google_PersistentDisk_" in dev or "google-" in dev:
            id = self.gce_disk_id(dev)
        else:
            id = self.scsi_id(dev)
        if len(id) == 0:
            return self.prefix_local(dev.replace('/dev/','').replace('/','!'))
        return id

    def get_gce_instance_data(self):
        if hasattr(self, "cache_instance_data"):
            return self.cache_instance_data
        cmd = ["gcloud", "compute", "instances", "describe", "-q", "--format", "json", rcEnv.nodename]
        out, err, ret = justcall(cmd)
        import json
        self.cache_instance_data = json.loads(out)
        return self.cache_instance_data

    def gce_disk_id(self, dev):
        if "Google_PersistentDisk_" in dev:
            devname = dev.split("Google_PersistentDisk_")[-1]
        else:
            devname = dev.split("google-")[-1]
        gce_instance_data = self.get_gce_instance_data()
        for disk in gce_instance_data["disks"]:
            if disk["deviceName"] != devname:
                continue
            i = disk["source"].index("/project")
            return str(disk["source"][i:].replace("/projects", "").replace("/zones", "").replace("/disks", ""))

    def cciss_id(self, dev):
        if dev in self.disk_ids:
            return self.disk_ids[dev]
        if which('cciss_id'):
            cciss_id = 'cciss_id'
        else:
            return ""
        cmd = [cciss_id, dev]
        out, err, ret = justcall(cmd)
        if ret == 0:
            id = out.split('\n')[0]
            if id.startswith('3'):
                id = id[1:]
            else:
                id = self.prefix_local(id)
            self.disk_ids[dev] = id
            return id
        return ""

    def mpath_id(self, dev):
        self.load_mpath()
        if 'dev' not in self.mpath_h:
            return None
        return self.mpath_h(dev)

    def load_mpath_native(self):
        cmd = [rcEnv.syspaths.multipath, '-l']
        out, err, ret = justcall(cmd)
        if ret != 0:
            return
        lines = out.split('\n')
        if len(lines) == 0:
            return
        self.mpath_h = {}
        regex = re.compile('[(]*[0-9a-f]*[)]*')
        for line in lines:
            if len(line) > 0 and \
               line[0] not in (' ', '\\', '[', '`', '|'):
                l = line.split()
                if l[0].startswith("size="):
                    continue
                wwid = None
                for w in l:
                    w = w.strip("()")
                    if len(w) not in [17, 33]:
                        continue
                    if regex.match(w) is None:
                        continue
                    if w[0] in ("2,", "3", "5"):
                        wwid = w[1:]
            elif " sd" in line:
                l = line.split()
                for i, w in enumerate(l):
                    if w.startswith('sd'):
                        dev = "/dev/"+w
                        self.mpath_h[dev] = wwid

    def load_mpath(self):
        if hasattr(self, "mpath_h"):
            return self.mpath_h
        self.mpath_h = {}
        if which(rcEnv.syspaths.multipath):
            self.load_mpath_native()
        return self.mpath_h

    def scsi_id(self, dev):
        s = self._scsi_id(dev, ["-p", "0x83"])
        if len(s) == 0:
            s = self._scsi_id(dev, ["-p", "pre-spc3-83"])
        return s

    def _scsi_id(self, dev, args=[]):
        wwid = self.mpath_id(dev)
        if wwid is not None:
            return wwid
        if dev in self.disk_ids:
            return self.disk_ids[dev]
        if which('scsi_id'):
            scsi_id = 'scsi_id'
        elif which('/lib/udev/scsi_id'):
            scsi_id = '/lib/udev/scsi_id'
        else:
            return ""
        cmd = [scsi_id, '-g', '-u'] + args + ['-d', dev]
        out, err, ret = justcall(cmd)
        if ret == 0:
            id = out.split('\n')[0]
            if id.startswith('3') or id.startswith('2') or id.startswith('5'):
                id = id[1:]
            else:
                id = self.prefix_local(id)
            self.disk_ids[dev] = id
            return id
        sdev = dev.replace("/dev/", "/block/")
        cmd = [scsi_id, '-g', '-u'] + args + ['-s', sdev]
        out, err, ret = justcall(cmd)
        if ret == 0:
            id = out.split('\n')[0]
            if id.startswith('3') or id.startswith('2') or id.startswith('5'):
                id = id[1:]
            else:
                id = self.prefix_local(id)
            self.disk_ids[dev] = id
            return id
        return ""

    def devpath_to_sysname(self, devpath):
        devpath = os.path.realpath(devpath)
        return os.path.basename(devpath)

    def disk_vendor(self, dev):
        if 'cciss' in dev:
            return 'HP'
        s = ''
        dev = self.devpath_to_sysname(dev)
        if dev.startswith("sd"):
            dev = re.sub("[0-9]+$", "", dev)
        path = '/sys/block/%s/device/vendor' % dev
        if not os.path.exists(path):
            l = glob.glob("/sys/block/%s/slaves/*/device/vendor" % dev)
            if len(l) > 0:
                path = l[0]
        if not os.path.exists(path):
            return ""
        with open(path, 'r') as f:
            s = f.read()
            f.close()
        if '6900' in s:
            s = 'Red Hat'
        return s.strip()

    def disk_model(self, dev):
        if 'cciss' in dev:
            return 'VOLUME'
        s = ''
        vendor = self.disk_vendor(dev)
        dev = self.devpath_to_sysname(dev)
        if dev.startswith("sd"):
            dev = re.sub("[0-9]+$", "", dev)
        path = '/sys/block/%s/device/model' % dev
        if not os.path.exists(path):
            l = glob.glob("/sys/block/%s/slaves/*/device/model" % dev)
            if len(l) > 0:
                path = l[0]
        if not os.path.exists(path):
            if 'Red Hat' in vendor:
                return 'VirtIO'
            else:
                return ""
        with open(path, 'r') as f:
            s = f.read()
            f.close()
        return s.strip()

    def disk_size(self, dev):
        size = 0
        if '/dev/mapper/' in dev:
            try:
                statinfo = os.stat(dev)
            except:
                raise Exception("can not stat %s" % dev)
            dm = 'dm-' + str(os.minor(statinfo.st_rdev))
            path = '/sys/block/' + dm + '/size'
            if not os.path.exists(path):
                return 0
        else:
            path = dev.replace('/dev/', '/sys/block/')+'/size'
            if not os.path.exists(path):
                cmd = ['blockdev', '--getsize', dev]
                out, err, ret = justcall(cmd)
                if ret != 0:
                    return 0
                return int(math.ceil(1.*int(out)/2048))

        with open(path, 'r') as f:
            size = f.read()
            f.close()
        return int(math.ceil(1.*int(size)/2048))

    def print_diskinfo(self, disk):
        name = os.path.basename(disk)
        info = {
          'dev': '',
          'size': 0,
          'device/vendor': '',
          'device/model': '',
        }
        for i in info:
            i_f = os.path.join(disk, i)
            if not os.path.exists(i_f):
                continue
            with open(i_f, 'r') as f:
                info[i] = f.read().strip()
        if '6900' in info['device/vendor']:
            info['device/vendor'] = 'Red Hat'
            if info['device/model'] is '':
                info['device/model'] = 'VirtIO'
        info['hbtl'] = os.path.basename(os.path.realpath(os.path.join(disk, "device")))
        print(self.print_diskinfo_fmt%(
          info['hbtl'],
          name,
          int(float(info['size'])/2//1024),
          info['dev'],
          info['device/vendor'],
          info['device/model'],
        ))

    def hba_num(self, hba=None):
        if hba is None:
            return
        if hba.startswith("iqn"):
            for path in glob.glob("/sys/class/scsi_host/host*"):
                for _path in glob.glob(path+"/device/session*/iscsi_session/session*/initiatorname"):
                    with open(_path, "r") as f:
                        content = f.read().strip()
                    if content == hba:
                        return os.path.basename(path).replace("host", "")
        for path in glob.glob("/sys/class/fc_host/host*"):
            for _path in glob.glob(path+"/port_name"):
                with open(_path, "r") as f:
                    content = f.read().strip()
                if content == hba or "0x"+content == hba:
                    return os.path.basename(path).replace("host", "")

    def target_num(self, host_num, target=None):
        if target is None:
            return
        if target.startswith("iqn"):
            for path in glob.glob("/sys/class/scsi_host/host"+str(host_num)+"/device/session*"):
                for _path in glob.glob(path+"/iscsi_session/session1/targetname"):
                    with open(_path, "r") as f:
                        content = f.read().strip()
                    if content == target:
                        path = glob.glob(path+"/target*:*:*")[0]
                        return path.split(":")[-1]
        for path in glob.glob("/sys/class/fc_transport/target%s:*:*" % str(host_num)):
            for _path in glob.glob(path+"/port_name"):
                with open(_path, "r") as f:
                    content = f.read().strip()
                if content == target or "0x"+content == target:
                    return os.path.basename(path).split(":")[-1]

    def scanscsi(self, hba=None, target=None, lun=None):
        if not os.path.exists('/sys') or not os.path.ismount('/sys'):
            print("scanscsi is not supported without /sys mounted", file=sys.stderr)
            return 1

        disks_before = glob.glob('/sys/block/sd*')
        disks_before += glob.glob('/sys/block/vd*')

        hba_num = self.hba_num(hba)
        if hba_num is not None:
            hosts = glob.glob('/sys/class/scsi_host/host'+str(hba_num))
            target_num = self.target_num(hba_num, target)
        else:
            hosts = glob.glob('/sys/class/scsi_host/host*')
            target_num = None

        if target_num is None:
            target_num = '-'

        if lun is None:
            lun = '-'

        for host in hosts:
            scan_f = host+'/scan'
            if not os.path.exists(scan_f):
                continue
            print("scan", os.path.basename(host), "target"+target_num, "lun"+lun)
            os.system('echo - ' + target_num + ' ' + lun + ' >' + scan_f)

        udevadm_settle()
        disks_after = glob.glob('/sys/block/sd*')
        disks_after += glob.glob('/sys/block/vd*')
        new_disks = set(disks_after) - set(disks_before)

        if len(new_disks) == 0:
            print("no new disk found")
            return 0

        self.print_diskinfo_header()
        #for disk in disks_before:
        for disk in new_disks:
            self.print_diskinfo(disk)

        return 0

if __name__ == "__main__":
    diskinfo = diskInfo()
    disks = glob.glob('/sys/block/sd*')
    disks += glob.glob('/sys/block/vd*')
    diskinfo.print_diskinfo_header()
    for disk in disks:
         diskinfo.print_diskinfo(disk)
    #dev = '/dev/vda'
    #vendor = diskinfo.disk_vendor(dev)
    #model = diskinfo.disk_model(dev)
    #print("%s has vendor [%s] model [%s]" % (dev, vendor, model))
   0707010005c930000081b40000000000000000000000015ab2175400000b32000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/rcDiskInfoOSF1.py  from __future__ import print_function
from rcUtilities import justcall
import rcDiskInfo
import re

regex = re.compile("^\W*[0-9]*:")
regex_path = re.compile('\W*[0-9]*\W+')

class diskInfo(rcDiskInfo.diskInfo):

    def __init__(self):
        self.load_cache()

    def is_id(self, line):
        if regex.match(line) is None:
            return False
        return True

    def cache_add(self, id, dev, wwid, path_count):
        d = self.devattr(id)
        vid = d['manufacturer']
        pid = d['model']
        size = d['mb']
        self.h[dev] = dict(
          wwid=wwid,
          vid=vid,
          pid=pid,
          size=size,
          id=id,
          path_count=path_count
        )

    def load_cache(self):
        self.h = {}
        cmd = ["hwmgr", "show", "scsi", "-type", "disk", "-active", "-full"]
        out, err, ret = justcall(cmd)
        path_count = -1
        for e in out.split('\n'):
            if len(e) == 0:
                continue
            if self.is_id(e):
                if path_count >= 0:
                    self.cache_add(id, dev, wwid, path_count)
                l = e.split()
                if len(l) < 8:
                    continue
                id = l[0].strip(':')
                dev = l[7]
                path_count = 0
            elif 'WWID' in e:
                wwid = e.split(":")[-1].replace('-','').lower()
                wwid = wwid.strip('"').replace(" ", "_")
            elif regex_path.match(e) is not None and 'valid' in e:
                path_count += 1
        if path_count >= 0:
            self.cache_add(id, dev, wwid, path_count)

    def devattr(self, id):
        d = {'capacity': 0, 'block_size': 0, 'manufacturer': '', 'model': '', 'mb': 0}
        cmd = ["hwmgr", "get", "att", "-id", id,
               "-a", "model",
               "-a", "manufacturer",
               "-a", "capacity",
               "-a", "block_size"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return d
        for line in out.split("\n"):
            if not line.startswith(' '):
                continue
            l = line.split('=')
            if len(l) !=2:
                continue
            d[l[0].strip()] = l[1].strip()
        d['mb'] = int(d['capacity']) * int(d['block_size']) // 1024 // 1024
        return d

    def get(self, dev, type):
        dev = dev.replace('/dev/rdisk/','')
        dev = dev.replace('/dev/disk/','')
        if dev not in self.h:
            return
        return self.h[dev][type]

    def disk_id(self, dev):
        return self.get(dev, 'wwid')

    def disk_vendor(self, dev):
        return self.get(dev, 'vid')

    def disk_model(self, dev):
        return self.get(dev, 'pid')

    def disk_size(self, dev):
        return self.get(dev, 'size')

if __name__ == "__main__":
    di = diskInfo()
    print(di.h.items())
  0707010005c931000081b40000000000000000000000015ab21754000013f2000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/rcDiskInfoSunOS.py import os
import rcDiskInfo
import rcDevTreeVeritas
from rcUtilities import justcall
import math
from rcGlobalEnv import rcEnv
from rcZone import is_zone

class diskInfo(rcDiskInfo.diskInfo):
    h = {}
    done = []

    def get_val(self, line):
        l = line.split(":")
        if len(l) != 2:
            return
        return l[-1].strip()

    def get_part_size(self, dev):
        part = dev[-1]
        size = 0
        cmd = ['prtvtoc', dev]
        (out, err, ret) = justcall(cmd)
        if ret != 0:
            return size

        bytes_per_sect = 0
        for line in out.split('\n'):
            if not line.startswith('*'):
                continue
            if "bytes/sector" in line:
                bytes_per_sect = int(line.split()[1])

        if bytes_per_sect == 0:
            return 0

        for line in out.split('\n'):
            if line.startswith('*'):
                continue

            l = line.split()
            if len(l) != 6:
                continue

            if l[0] != part:
                continue

            return math.ceil(1.*int(l[4])*bytes_per_sect/1024/1024)

        return 0

    def get_size(self, dev):
        size = 0
        dev = dev.replace("/dev/dsk/", "/dev/rdsk/")
        dev = dev.replace("/dev/vx/dmp/", "/dev/vx/rdmp/")
        cmd = ['prtvtoc', dev]
        (out, err, ret) = justcall(cmd)
        if ret != 0:
            return size

        """
        *     512 bytes/sector
        *      63 sectors/track
        *     255 tracks/cylinder
        *   16065 sectors/cylinder
        *   19581 cylinders
        *   19579 accessible cylinders
        **  OR:
        *   188743612 accessible sectors
        """
        for line in out.split('\n'):
            if not line.startswith('*'):
                continue
            try:
                if "bytes/sector" in line:
                    n1 = int(line.split()[1])
                if "accessible sectors" in line:
                    s0 = int(line.split()[1])
                    size = math.ceil(1. * s0 * n1 / 1024 / 1024)
                    break
                if "sectors/cylinder" in line:
                    n2 = int(line.split()[1])
                if "cylinders" in line:
                    n3 = int(line.split()[1])
                size = math.ceil(1. * n1 * n2 * n3 / 1024 / 1024)
            except:
                pass

        return size

    def __init__(self, deferred=False):
        self.zone = is_zone()
        self.deferred = deferred
        if deferred:
            return
        self.scan()

    def scan(self):
        if 'scan' in self.done:
            return
        self.done.append('scan')
        cmd = ["/usr/bin/find", "/dev/rdsk", "-name", "c*s2"]
        (out, err, ret) = justcall(cmd)
        if ret != 0:
            return
        lines = out.split('\n')
        if len(lines) < 2:
            return
        for e in lines:
            if "/dev/" not in e:
                continue

            dev = e.strip()
            self.scan_dev(dev)

    def scan_dev(self, dev):
        dev = dev.replace("/dev/vx/dmp/", "/dev/vx/rdmp/")
        if "dmp/" in dev:
            tree = rcDevTreeVeritas.DevTreeVeritas()
            wwid = tree.vx_inq(dev)
            vid = tree.vx_vid(dev)
            pid = tree.vx_pid(dev)
            size = 0
        else:
            cmd = ["mpathadm", "show", "lu", dev]
            (out, err, ret) = justcall(cmd)
            if ret != 0:
                return
            if "Error: Logical-unit " + dev + " is not found" in err:
                dsk = dev.replace("/dev/rdsk/", "")
                dsk = dsk.replace("s2", "")
                wwid = rcEnv.nodename + "." + dsk
                vid = "LOCAL"
                pid = ""
                size = 0
            else:
                wwid = ""
                vid = ""
                pid = ""
                size = 0

                for line in out.split('\n'):
                    if line.startswith("\tVendor:"):
                        vid = self.get_val(line)
                    elif line.startswith("\tProduct:"):
                        pid = self.get_val(line)
                    elif line.startswith("\tName:"):
                        wwid = self.get_val(line)

        size = self.get_size(dev)
        self.h[dev] = dict(wwid=wwid, vid=vid, pid=pid, size=size)

    def get(self, dev, type):
        dev = dev.replace("/dev/vx/dmp/", "/dev/vx/rdmp/")
        if self.deferred or dev not in self.h:
            self.scan_dev(dev)
        dummy = dict(wwid="unknown", vid="unknown", pid="unknown", size=0)
        if dev not in self.h:
            if self.zone:
                return None
            return dummy[type]
        return self.h[dev][type]

    def disk_id(self, dev):
        return self.get(dev, 'wwid')

    def disk_vendor(self, dev):
        return self.get(dev, 'vid')

    def disk_model(self, dev):
        return self.get(dev, 'pid')

    def disk_size(self, dev):
        return self.get(dev, 'size')

    def scanscsi(self, hba=None, target=None, lun=None):
        os.system("cfgadm -al")
  0707010005c932000081b40000000000000000000000015ab2175400000e0e000000660000000000000000000000000000003000000000root/usr/share/opensvc/lib/rcDiskInfoWindows.py   import rcDiskInfo
import wmi
from rcUtilities import justcall, which

class diskInfo(rcDiskInfo.diskInfo):

    def __init__(self):
        self.h = {}
        self.fcluns = {}
        self.wmi = wmi.WMI()

    def scan_mapping(self):
        if len(self.fcluns) > 0:
            return

        if not which('fcinfo'):
            return

        for index, portwwn, host in self._get_fc_hbas():
            cmd = ['fcinfo', '/mapping', '/ai:'+index]
            out, err, ret = justcall(cmd)
            if ret != 0:
                continue
            lines = out.split('\n')
            for i, line in enumerate(lines):
                if line.startswith('(  '):
                    l = line.split()
                    if len(l) < 3:
                        continue
                    bus = int(l[-3].strip(','))
                    target = int(l[-2].strip(','))
                    lun = int(l[-1].strip(')'))
                    _index = (host, bus, target, lun)
                elif line.startswith('(cs:'):
                    l = line.split()
                    if len(l) < 2:
                        continue
                    wwid = l[-1].strip(')')
                    self.fcluns[_index] = dict(wwid=wwid)

    def scan(self):
        self.scan_mapping()

        vid = 'unknown'
        pid = 'unknown'
        wwid = 'unknown'
        size = 'unknown'

        for drive in self.wmi.WIN32_DiskDrive():
            id = drive.DeviceID
            vid = str(drive.Manufacturer)
            pid = str(drive.Caption)
            try:
                serial = str(drive.SerialNumber)
            except:
                serial = "unknown"
            size = int(drive.Size) // 1024 // 1024
            host = drive.SCSIPort
            bus = drive.SCSIBus
            target = drive.SCSITargetId
            lun = drive.SCSILogicalUnit


            d = dict(id=id,
                     vid=vid,
                     pid=pid,
                     wwid=wwid,
                     serial=serial,
                     host=host,
                     bus=bus,
                     target=target,
                     lun=lun,
                     size=size)

            d['wwid'] = self.get_wwid(d)
            if d['wwid'] is None:
                d['wwid'] = d['serial']

            self.h[id] = d

    def get_wwid(self, d):
        index = (d['host'], d['bus'], d['target'], d['lun'])
        if index not in self.fcluns:
            return None
        return self.fcluns[index]['wwid']

    def get(self, id, prop):
        if len(self.h) == 0:
            self.scan()
        if id not in self.h:
            return None
        return self.h[id][prop]

    def disk_id(self, dev):
        return self.get(dev, 'wwid')

    def disk_vendor(self, dev):
        return self.get(dev, 'vid')

    def disk_model(self, dev):
        return self.get(dev, 'pid')

    def disk_size(self, dev):
        return self.get(dev, 'size')

    def _get_fc_hbas(self):
        hbas = []
        if not which('fcinfo'):
            return []
        cmd = ['fcinfo']
        out, err, ret = justcall(cmd)
        if ret != 0:
            return []
        for line in out.split('\n'):
            if 'PortWWN' not in line:
                continue
            l = line.split()
            i = l.index('PortWWN:')
            if len(l) < i+2:
                continue
            index = l[0].split('-')[-1].strip(':')
            portwwn = l[i+1].replace(':', '')
            host = int(l[-1].split('Scsi')[-1].strip(':'))
            hbas.append((index, portwwn, host))
        return hbas

  0707010005c934000081b40000000000000000000000015ab21754000012d1000000660000000000000000000000000000002700000000root/usr/share/opensvc/lib/rcEmcVnx.py    from __future__ import print_function

import os
import json
from subprocess import *
import time

try:
    import ConfigParser
except ImportError:
    import configparser as ConfigParser

import rcExceptions as ex
from rcGlobalEnv import rcEnv
from rcUtilities import justcall, which

if rcEnv.paths.pathbin not in os.environ['PATH']:
    os.environ['PATH'] += ":"+rcEnv.paths.pathbin

def naviseccli(cmd, scope=None, spa=None, spb=None, username=None, password=None):
    if which('/opt/Navisphere/bin/naviseccli') is None:
        raise ex.excError('can not find Navicli programs in usual /opt/Navisphere/bin')

    _cmd = ['/opt/Navisphere/bin/naviseccli', '-h', spa]
    _cmd += cmd
    out, err, ret = justcall(_cmd)
    if "Security file not found" in out:
        print(_cmd)
        print(out)
        raise ex.excError("naviseccli command execution error")

    return out, err

class EmcVnxs(object):
    allowed_methods = ("secfile", "credentials")

    def __init__(self, objects=[]):
        self.objects = objects
        if len(objects) > 0:
            self.filtering = True
        else:
            self.filtering = False
        self.arrays = []
        cf = rcEnv.paths.authconf
        if not os.path.exists(cf):
            return
        conf = ConfigParser.RawConfigParser()
        conf.read(cf)
        m = {}

        for s in conf.sections():
            if not conf.has_option(s, "type") or \
               conf.get(s, "type") != "emcvnx":
                continue

            if self.filtering and not s in self.objects:
                continue

            spa = None
            spb = None
            username = None
            password = None
            scope = None

            kwargs = {}

            try:
                method = conf.get(s, 'method')
            except:
                method = "secfile"

            if method not in self.allowed_methods:
                print("invalid method. allowed methods: %s" % ', '.join(self.allowed_methods))
                continue

            try:
                spa = conf.get(s, 'spa')
                spb = conf.get(s, 'spb')
            except:
                print("error parsing section", s)
                continue

            try:
                username = conf.get(s, 'username')
                password = conf.get(s, 'password')
                kwargs['username'] = username
                kwargs['password'] = password
            except:
                if method in ("credentials"):
                    print("error parsing section", s)
                    continue

            try:
                scope = conf.get(s, 'scope')
            except:
                scope = "0"

            self.arrays.append(EmcVnx(s, method, scope, spa, spb, **kwargs))

        del(conf)

    def __iter__(self):
        for array in self.arrays:
            yield(array)

class EmcVnx(object):
    def __init__(self, name, method, scope, spa, spb, username=None, password=None):
        self.name = name
        self.spa = spa
        self.spb = spb
        self.method = method
        self.scope = scope
        self.username = username
        self.password = password
        self.keys = ['portlistsp', 'getall', 'metalunlist', 'getalllun', 'getagent', 'getarrayuid', 'storagepool', 'thinlunlistall', 'getall', 'getallrg']

    def rcmd(self, cmd, log=None):
        if self.method in 'secfile':
            return naviseccli(cmd, self.scope, self.spa, self.spb, None, None)
        else:
            return naviseccli(cmd, self.scope, self.spa, self.spb, self.username, self.password)

    def get_portlistsp(self):
        cmd = ['port', '-list']
        s = self.rcmd(cmd)[0]
        return s

    def get_getall(self):
        cmd = ['getall']
        s = self.rcmd(cmd)[0]
        return s

    def get_getallrg(self):
        cmd = ['getall', '-rg']
        s = self.rcmd(cmd)[0]
        return s

    def get_metalunlist(self):
        cmd = ['metalun', '-list']
        s = self.rcmd(cmd)[0]
        return s

    def get_getalllun(self):
        cmd = ['getall' ,'-lun']
        s = self.rcmd(cmd)[0]
        return s

    def get_getagent(self):
        cmd = ['getagent']
        s = self.rcmd(cmd)[0]
        return s

    def get_getarrayuid(self):
        cmd = ['getarrayuid']
        s = self.rcmd(cmd)[0]
        return s

    def get_storagepool(self):
        cmd = ['storagepool', '-list', '-all']
        s = self.rcmd(cmd)[0]
        return s

    def get_thinlunlistall(self):
        cmd = ['thinlun', '-list', '-all']
        s = self.rcmd(cmd)[0]
        return s

if __name__ == "__main__":
    o = EmcVnxs()
    for emcvnx in o:
        print(emcvnx.get_portlistsp())
        #print(emcvnx.get_sglist())
        #print(emcvnx.get_getalllun())
        #print(emcvnx.get_metalunlist())
   0707010005c935000081b40000000000000000000000015ab21754000006c9000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/rcEthtool.py   from rcUtilities import justcall, which

"""
Settings for eth0:
        Supported ports: [ TP ]
        Supported link modes:   1000baseT/Full
                                10000baseT/Full
        Supports auto-negotiation: Yes
        Advertised link modes:  1000baseT/Full
                                10000baseT/Full
        Advertised pause frame use: No
        Advertised auto-negotiation: No
        Speed: 5000Mb/s
        Duplex: Full
        Port: Twisted Pair
        PHYAD: 0
        Transceiver: internal
        Auto-negotiation: on
        MDI-X: Unknown
        Supports Wake-on: g
        Wake-on: d
        Link detected: yes
"""

class LoadError(Exception):
    pass

class Ethtool(object):
    def __init__(self, intf):
        self.intf = intf
        self.data = {}

    def __getattr__(self, attr):
        if len(self.data) == 0:
            self.load()
        if attr not in self.data:
            return None
        return self.data[attr]

    def load(self):
        if not which('ethtool'):
            raise LoadError("ethtool is not installed")
        cmd = ['ethtool', self.intf]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise LoadError("ret=%d\nout=%s\nerr=%s\n"%(ret, out, err))
        for line in out.split('\n'):
            if not line.startswith('\t'):
                continue
            l = line.split(': ')
            if len(l) != 2:
                continue
            param = l[0].strip().replace(" ", "_").replace("-", "_").lower()
            value = l[1].strip()
            self.data[param] = value

if __name__ == "__main__":
    o = Ethtool("eth0")
    o.load()
    for attr in o.data.keys():
        print("%-30s: %s"%(attr, str(o.data[attr])))

   0707010005c936000081b40000000000000000000000015ab21754000011ab000000660000000000000000000000000000002400000000root/usr/share/opensvc/lib/rcEva.py   from __future__ import print_function

import os
from xml.etree.ElementTree import XML, fromstring

try:
    import ConfigParser
except ImportError:
    import configparser as ConfigParser

import rcExceptions as ex
from rcGlobalEnv import rcEnv
from rcUtilities import justcall, which

def sssu(cmd, manager, username, password, array=None, sssubin=None):
    if sssubin is None:
        if which("sssu"):
            sssubin = "sssu"
        elif os.path.exists(os.path.join(rcEnv.paths.pathbin, "sssu")):
            sssubin = os.path.join(rcEnv.paths.pathbin, "sssu")
        else:
            raise ex.excError("sssu command not found. set 'bin' in auth.conf section.")
    os.chdir(rcEnv.paths.pathtmp)
    _cmd = [sssubin,
            "select manager %s username=%s password=%s"%(manager, username, password)]
    if array is not None:
        _cmd += ["select system %s"%array]
    _cmd += [cmd]
    out, err, ret = justcall(_cmd)
    print(" ".join(_cmd))
    if "Error" in out:
        print(_cmd)
        print(out)
        raise ex.excError("sssu command execution error")
    return out, err

class Evas(object):
    arrays = []

    def __init__(self, objects=[]):
        self.objects = objects
        if len(objects) > 0:
            self.filtering = True
        else:
            self.filtering = False
        cf = rcEnv.paths.authconf
        if not os.path.exists(cf):
            return
        conf = ConfigParser.RawConfigParser()
        conf.read(cf)
        m = {}
        for s in conf.sections():
            try:
                t = conf.get(s, 'type')
            except:
                continue
            if t != "eva":
                continue
            try:
                manager = conf.get(s, 'manager')
                username = conf.get(s, 'username')
                password = conf.get(s, 'password')
            except Exception as e:
                print("error parsing section", s, ":", e)
                pass
            try:
                sssubin = conf.get(s, 'bin')
            except:
                sssubin = None
            m[manager] = [username, password, sssubin]
        del(conf)
        done = []
        for manager, creds in m.items():
            username, password, sssbin = creds
            out, err = sssu('ls system', manager, username, password, sssubin=sssubin)
            _in = False
            for line in out.split('\n'):
                if 'Systems avail' in line:
                    _in = True
                    continue
                if not _in:
                    continue
                name = line.strip()
                if self.filtering and name not in self.objects:
                    continue
                self.arrays.append(Eva(name, manager, username, password, sssubin=sssubin))
                done.append(name)

    def __iter__(self):
        for array in self.arrays:
            yield(array)

class Eva(object):
    def __init__(self, name, manager, username, password, sssubin=None):
        self.name = name
        self.manager = manager
        self.username = username
        self.password = password
        self.sssubin = sssubin
        #self.keys = ['disk_group']
        self.keys = ['controller', 'disk_group', 'vdisk']

    def sssu(self, cmd):
        return sssu(cmd, self.manager, self.username, self.password, array=self.name, sssubin=self.sssubin)

    def stripxml(self, buff):
        try:
            buff = buff[buff.index("<object>"):]
        except:
            buff = ""
        lines = buff.split('\n')
        for i, line in enumerate(lines):
            if line.startswith("\\"):
                del lines[i]
        lines = ['<main>'] + lines + ['</main>']
        return '\n'.join(lines)

    def get_controller(self):
        cmd = 'ls controller full xml'
        print("%s: %s"%(self.name, cmd))
        buff = self.sssu(cmd)[0]
        return self.stripxml(buff)

    def get_disk_group(self):
        cmd = 'ls disk_group full xml'
        print("%s: %s"%(self.name, cmd))
        buff = self.sssu(cmd)[0]
        return self.stripxml(buff)

    def get_vdisk(self):
        cmd = 'ls vdisk full xml'
        print("%s: %s"%(self.name, cmd))
        buff = self.sssu(cmd)[0]
        return self.stripxml(buff)

    def get_lun(self):
        cmd = 'ls lun full xml'
        print("%s: %s"%(self.name, cmd))
        buff = self.sssu(cmd)[0]
        return self.stripxml(buff)

if __name__ == "__main__":
    o = Evas()
    for eva in o:
        print(eva.get_controller())
 0707010005c937000081b40000000000000000000000015ab2175400000db1000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/rcExceptions.py    class excEncapUnjoignable(Exception):
    pass

class excError(Exception):
    """ Failed action
    """
    def __init__(self, value=""):
        self.value = value
    def __str__(self):
        return str(self.value)

class OptNotFound(Exception):
    """
    Service config file option not found.
    Raised by Svc::conf_get().
    The default property is used to propagate the keyword default value.
    """
    def __init__(self, value="", default=None):
        self.value = value
        self.default = default
    def __str__(self):
        return "%s default: %s" % (str(self.value), repr(self.default))

class RequiredOptNotFound(Exception):
    """
    Required service config file option not found.
    Raised by Svc::conf_get().
    """
    pass

class excSignal(Exception):
    """ Termination signal received
    """

class excUndefined(Exception):
    """ Mandatory Undefined action exception
    """
    def __init__(self,action=None,className=None,func=None):
        self.action=action
        self.className=className
        self.func=func
    def __str__(self):
        return "Undefined mandatory Action %s for className %s in function %s" % \
                (self.action,self.className,self.func)

class syncNoNodesToSync(Exception):
    """ No nodes to sync => abort stacked syncs resource actions
    """

class syncNoFilesToSync(Exception):
    """ No files to sync => move on to the next stacked syncs resource actions
    """

class syncConfigSyntaxError(Exception):
    """ Bogus configuration syntax => abort all
    """

class syncNotSnapable(Exception):
    """ A dir/file specified as source of a sync with snap is not included in a
        snapable resource mount => abort all
    """

class syncSnapExists(Exception):
    """ The snapshot already exists
    """

class syncSnapCreateError(Exception):
    """ Error in snapshot creation => clean up
    """

class syncSnapDestroyError(Exception):
    """ Error in snapshot destroy => clean up
    """

class syncSnapMountError(Exception):
    """ Error mounting fs => clean up
    """

class excEndAction(Exception):
    """ End multi-resource action. Not an error.
    """

class excContinueAction(Exception):
    """ Abort current resource action, but proceed anyway
    """

class excAbortAction(Exception):
    """ Abort multi-resource action
    """

class excInitError(Exception):
    """ Resource initialisation error
    """

class excScsiPrNotsupported(Exception):
    """ Scsi persistent reservation is not supported
    """

class excNotAvailable(Exception):
    """ Not available
    """

class excNotSupported(Exception):
    """ Not supported
    """

class excBug(Exception):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self.value)

class MissImpl(Exception):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self.value)

class IpDevDown(Exception):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self.value)

class IpConflict(Exception):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self.value)

class IpAlreadyUp(Exception):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self.value)

class IpNoActions(Exception):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self.value)
   0707010005c938000081b40000000000000000000000015ab2175400007d10000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/rcFreenas.py   from __future__ import print_function

import sys
import os
import json
from optparse import Option

import rcExceptions as ex
from rcConfigParser import RawConfigParser
from rcGlobalEnv import rcEnv, Storage
from rcUtilities import bdecode
from converters import convert_size
from rcOptParser import OptParser

try:
    import requests
except ImportError:
    raise ex.excInitError("the requests module must be installed")

try:
    requests.packages.urllib3.disable_warnings()
except:
    pass

VERIFY = False

PROG = "nodemgr array"
OPT = Storage({
    "help": Option(
        "-h", "--help", default=None, action="store_true", dest="parm_help",
        help="show this help message and exit"),
    "array": Option(
        "-a", "--array", default=None, action="store", dest="array_name",
        help="The name of the array, as defined in auth.conf"),
    "name": Option(
        "--name", default=None, action="store", dest="name",
        help="The object name"),
    "volume": Option(
        "--volume", default=None, action="store", dest="volume",
        help="The volume to create the disk into"),
    "size": Option(
        "--size", default="0", action="store", dest="size",
        help="The disk size, expressed as a size expression like 1g, 100mib, ..."),
    "target": Option(
        "--target", action="append", dest="targets",
        help="A target name to export the disk through. Can be set multiple times."),
    "blocksize": Option(
        "--blocksize", default=512, type=int, action="store", dest="blocksize",
        help="The exported disk blocksize in B"),
    "secure_tpc": Option(
        "--secure-tpc", default=True, action="store_false", dest="insecure_tpc",
        help="Set the insecure_tpc flag to False"),
    "compression": Option(
        "--compression", default="on", action="store", dest="compression",
        choices=["on", "off", "inherit", "lzjb", "lz4", "gzip", "gzip-9", "zle"],
        help="Toggle compression"),
    "dedup": Option(
        "--dedup", default="off", action="store", dest="dedup", choices=["on", "off"],
        help="Toggle dedup"),
    "naa": Option(
        "--naa", default=None, action="store", dest="naa",
        help="The disk naa identifier"),
    "initiator": Option(
        "--initiator", action="append", dest="initiators",
        help="An initiator iqn. Can be specified multiple times."),
    "auth_network": Option(
        "--auth-network", default="ALL", action="store", dest="auth_network",
        help="Network authorized to access to the iSCSI target. ip or cidr addresses or 'ALL' for any ips"),
    "comment": Option(
        "--comment", action="store", dest="comment",
        help="Description for your reference"),
    "id": Option(
        "--id", action="store", type=int, dest="id",
        help="An object id, as reported by a list action"),
    "alias": Option(
        "--alias", action="store", dest="alias",
        help="An object name alias"),
    "target_id": Option(
        "--target-id", action="store", type=int, dest="target_id",
        help="The target object id"),
    "authgroup_id": Option(
        "--auth-group-id", action="store", type=int, dest="authgroup_id",
        help="The auth group object id"),
    "authtype": Option(
        "--auth-type", action="store", default="None", dest="authtype",
        choices=["None", "CHAP", "CHAP Mutual"],
        help="None, CHAP, CHAP Mutual"),
    "portal_id": Option(
        "--portal-id", action="store", type=int, dest="portal_id",
        help="The portal object id"),
    "initiatorgroup_id": Option(
        "--initiatorgroup-id", action="store", type=int, dest="initiatorgroup_id",
        help="The initiator group object id"),
    "mappings": Option(
        "--mappings", action="append", dest="mappings",
        help="A <hba_id>:<tgt_id>,<tgt_id>,... mapping used in add map in replacement of --targetgroup and --initiatorgroup. Can be specified multiple times."),
})

GLOBAL_OPTS = [
    OPT.array,
]

DEPRECATED_ACTIONS = []

ACTIONS = {
    "Add actions": {
        "add_iscsi_file": {
            "msg": "Add and present a file-backed iscsi disk",
            "options": [
                OPT.name,
                OPT.volume,
                OPT.size,
                OPT.target,
                OPT.blocksize,
                OPT.secure_tpc,
                OPT.mappings,
            ],
        },
        "add_iscsi_zvol": {
            "msg": "Add and present a zvol-backed iscsi disk",
            "options": [
                OPT.name,
                OPT.volume,
                OPT.size,
                OPT.target,
                OPT.blocksize,
                OPT.secure_tpc,
                OPT.compression,
                OPT.dedup,
                OPT.mappings,
            ],
        },
        "add_iscsi_initiatorgroup": {
            "msg": "Declare a group of iscsi initiator iqn, for use in targetgroups which are portal-target-initiator relations",
            "options": [
                OPT.initiator,
                OPT.comment,
                OPT.auth_network,
            ],
        },
        "add_iscsi_target": {
            "msg": "Declare a iscsi target, for use in targetgroups which are portal-target-initiator relations",
            "options": [
                OPT.name,
                OPT.alias,
            ],
        },
        "add_iscsi_targetgroup": {
            "msg": "Declare a iscsi target group, which is a portal-target-initiator relation",
            "options": [
                OPT.portal_id,
                OPT.target_id,
                OPT.initiatorgroup_id,
                OPT.authgroup_id,
                OPT.authtype,
            ],
        },
    },
    "Delete actions": {
        "del_iscsi_file": {
            "msg": "Delete and unpresent a file-backed iscsi disk",
            "options": [
                OPT.name,
                OPT.naa,
            ],
        },
        "del_iscsi_zvol": {
            "msg": "Delete and unpresent a zvol-backed iscsi disk",
            "options": [
                OPT.name,
                OPT.naa,
            ],
        },
        "del_iscsi_initiatorgroup": {
            "msg": "Delete a group of iscsi initiator iqn, used in targets which are portal-target-initiator relations",
            "options": [
                OPT.id,
            ],
        },
        "del_iscsi_target": {
            "msg": "Delete a iscsi target, used in targets which are portal-target-initiator relations",
            "options": [
                OPT.id,
            ],
        },
        "del_iscsi_targetgroup": {
            "msg": "Delete a iscsi target group, which is a portal-target-initiator relation",
            "options": [
                OPT.id,
            ],
        },
    },
    "Modify actions": {
        "resize_zvol": {
            "msg": "Resize a zvol",
            "options": [
                OPT.name,
                OPT.naa,
                OPT.size,
            ],
        },
    },
    "List actions": {
        "list_mappings": {
            "msg": "List configured volumes",
            "options": [
                OPT.name,
                OPT.naa,
            ],
        },
        "list_volume": {
            "msg": "List configured volumes",
        },
        "list_iscsi_portal": {
            "msg": "List configured portals",
        },
        "list_iscsi_target": {
            "msg": "List configured targets",
        },
        "list_iscsi_targetgroup": {
            "msg": "List configured target groups",
        },
        "list_iscsi_targettoextent": {
            "msg": "List configured target-to-extent relations",
        },
        "list_iscsi_extent": {
            "msg": "List configured extents",
        },
        "list_iscsi_initiatorgroup": {
            "msg": "List configured initiator groups",
        },
    },
}

class Freenass(object):
    arrays = []

    def __init__(self, objects=[]):
        self.objects = objects
        self.filtering = len(objects) > 0
        cf = rcEnv.paths.authconf
        if not os.path.exists(cf):
            return
        conf = RawConfigParser()
        conf.read(cf)
        m = []
        for s in conf.sections():
            try:
                stype = conf.get(s, 'type')
            except:
                continue
            if stype != "freenas":
                continue
            try:
                name = s
                api = conf.get(s, 'api')
                username = conf.get(s, 'username')
                password = conf.get(s, 'password')
                m += [(name, api, username, password)]
            except:
                print("error parsing section", s)
        del conf
        done = []
        for name, api, username, password in m:
            if self.filtering and name not in self.objects:
                continue
            if name in done:
                continue
            self.arrays.append(Freenas(name, api, username, password))
            done.append(name)

    def __iter__(self):
        for array in self.arrays:
            yield(array)

    def get_freenas(self, name):
        for array in self.arrays:
            if array.name == name:
                return array
        return None

class Freenas(object):
    def __init__(self, name, api, username, password):
        self.node = None
        self.name = name
        self.api = api
        self.username = username
        self.password = password
        self.auth = (username, password)
        self.keys = ['version',
                     'volumes',
                     'iscsi_targets',
                     'iscsi_targettoextents',
                     'iscsi_extents']

    def delete(self, uri, data=None):
        api = self.api+uri+"/"
        headers = {'Content-Type': 'application/json'}
        r = requests.delete(api, data=json.dumps(data), auth=self.auth, verify=VERIFY, headers=headers)
        return r

    def put(self, uri, data=None):
        api = self.api+uri+"/"
        headers = {'Content-Type': 'application/json'}
        r = requests.put(api, data=json.dumps(data), auth=self.auth, verify=VERIFY, headers=headers)
        return bdecode(r.content)

    def post(self, uri, data=None):
        api = self.api+uri+"/"
        headers = {'Content-Type': 'application/json'}
        r = requests.post(api, data=json.dumps(data), auth=self.auth, verify=VERIFY, headers=headers)
        return bdecode(r.content)

    def post2(self, uri, data=None):
        api = self.api.replace("api/v1.0", "")+uri
        s = requests.Session()
        r = s.get(api)
        csrf_token = r.cookies['csrftoken']
        data["csrfmiddlewaretoken"] = csrf_token
        r = requests.post(api, data=data, auth=self.auth, verify=VERIFY)
        return bdecode(r.content)

    def get(self, uri, params=None):
        r = requests.get(self.api+uri+"/?format=json", params=params, auth=self.auth, verify=VERIFY)
        return bdecode(r.content)

    def get_version(self):
        buff = self.get("/system/version")
        return buff

    def get_volume(self, name):
        buff = self.get("/storage/volume/%s" % name, {"limit": 0})
        return buff

    def get_volume_datasets(self, name):
        buff = self.get("/storage/volume/%s/datasets" % name, {"limit": 0})
        return buff

    def get_volumes(self):
        buff = self.get("/storage/volume", {"limit": 0})
        return buff

    def get_iscsi_target_id(self, tgt_id):
        buff = self.get("/services/iscsi/target/%d" % tgt_id)
        return buff

    def get_iscsi_targets(self):
        buff = self.get("/services/iscsi/target", {"limit": 0})
        return buff

    def get_iscsi_targettoextents(self):
        buff = self.get("/services/iscsi/targettoextent", {"limit": 0})
        return buff

    def get_iscsi_extents(self):
        buff = self.get("/services/iscsi/extent", {"limit": 0})
        return buff

    def get_iscsi_portal(self):
        buff = self.get("/services/iscsi/portal", {"limit": 0})
        return buff

    def get_iscsi_targetgroup(self):
        buff = self.get("/services/iscsi/targetgroup", {"limit": 0})
        return buff

    def get_iscsi_targetgroup_id(self, tg_id):
        buff = self.get("/services/iscsi/targetgroup/%d" % tg_id)
        return buff

    def get_iscsi_authorizedinitiator(self):
        buff = self.get("/services/iscsi/authorizedinitiator", {"limit": 0})
        return buff

    def get_iscsi_authorizedinitiator_id(self, initiator_id):
        buff = self.get("/services/iscsi/authorizedinitiator/%d" % initiator_id)
        return buff

    def get_iscsi_target_ids(self, target_names):
        buff = self.get_iscsi_targets()
        data = json.loads(buff)
        l = []
        for target in data:
            if target["iscsi_target_name"] in target_names:
                l.append(target["id"])
        return l

    def get_iscsi_extents_data(self):
        buff = self.get_iscsi_extents()
        data = json.loads(buff)
        return data

    def get_iscsi_extent(self, naa=None, name=None):
        data = self.get_iscsi_extents_data()
        if naa and not naa.startswith("0x"):
            naa = "0x" + naa
        for extent in data:
            if name and name == extent["iscsi_target_extent_name"]:
                return extent
            if naa and naa == extent["iscsi_target_extent_naa"]:
                return extent

    def del_iscsi_extent(self, extent_id):
        path = "/services/iscsi/extent/%d" % extent_id
        response = self.delete(path)
        if response.status_code != 204:
            raise ex.excError("delete error: %s (%d)" % (path, response.status_code))

    def add_iscsi_zvol_extent(self, name=None, size=None, volume=None,
                              insecure_tpc=True, blocksize=512, **kwargs):
        for key in ["name", "size", "volume"]:
            if locals()[key] is None:
                raise ex.excError("'%s' key is mandatory" % key)
        data = self.add_zvol(name=name, size=size, volume=volume, **kwargs)
        d = {
            "iscsi_target_extent_type": "Disk",
            "iscsi_target_extent_name": name,
            "iscsi_target_extent_insecure_tpc": insecure_tpc,
            "iscsi_target_extent_blocksize": blocksize,
            "iscsi_target_extent_disk": "zvol/%s/%s" % (volume, name),
        }
        buff = self.post("/services/iscsi/extent", d)
        data = json.loads(buff)
        return data


    def add_iscsi_file_extent(self, name=None, size=None, volume=None,
                              insecure_tpc=True, blocksize=512, **kwargs):
        for key in ["name", "size", "volume"]:
            if locals()[key] is None:
                raise ex.excError("'%s' key is mandatory" % key)
        size = convert_size(size, _to="MB")
        d = {
            "iscsi_target_extent_type": "File",
            "iscsi_target_extent_name": name,
            "iscsi_target_extent_insecure_tpc": insecure_tpc,
            "iscsi_target_extent_blocksize": blocksize,
            "iscsi_target_extent_filesize": str(size)+"MB",
            "iscsi_target_extent_path": "/mnt/%s/%s" % (volume, name),
        }
        buff = self.post("/services/iscsi/extent", d)
        data = json.loads(buff)
        return data

    def add_iscsi_targets_to_extent(self, extent_id=None, targets=None,
                                    **kwargs):
        for key in ["extent_id", "targets"]:
            if locals()[key] is None:
                raise ex.excError("'%s' key is mandatory" % key)
        target_ids = self.get_iscsi_target_ids(targets)
        data = []
        for target_id in target_ids:
            data.append(self.add_iscsi_target_to_extent(target_id, extent_id))
        return data

    def add_iscsi_target_to_extent(self, target_id, extent_id):
        d = {
            "iscsi_target": target_id,
            "iscsi_extent": extent_id,
        }
        buff = self.post("/services/iscsi/targettoextent", d)
        data = json.loads(buff)
        return data

    def del_zvol(self, name=None, volume=None, **kwargs):
        for key in ["name", "volume"]:
            if locals()[key] is None:
                raise ex.excError("'%s' key is mandatory" % key)
        path = '/storage/volume/%s/zvols/%s' % (volume, name)
        response = self.delete(path)
        if response.status_code != 204:
            raise ex.excError("delete error: %s (%d)" % (path, response.status_code))

    def add_zvol(self, name=None, size=None, volume=None,
                 compression="inherit", dedup="off",
                 **kwargs):
        for key in ["name", "size", "volume"]:
            if locals()[key] is None:
                raise ex.excError("'%s' key is mandatory" % key)
        size = convert_size(size, _to="MB")
        d = {
            "name": name,
            "volsize": str(size)+"MB",
            "compression": compression,
            "dedup": dedup,
        }
        buff = self.post('/storage/volume/%s/zvols/' % volume, d)
        try:
            return json.loads(buff)
        except ValueError:
            raise ex.excError(buff)

    def get_zvol(self, volume=None, name=None):
        buff = self.get('/storage/volume/%s/zvols/%s' % (volume, name))
        try:
            return json.loads(buff)
        except ValueError:
            raise ex.excError(buff)

    def list_mappings(self, name=None, naa=None, **kwargs):
        tte_data = json.loads(self.get_iscsi_targettoextents())
        if name is not None or naa is not None:
            data = self.get_iscsi_extent(name=name, naa=naa)
            if data is None:
                raise ex.excError("extent not found")
            extent_id = data["id"]
            tte_data = [d for d in tte_data if d["iscsi_extent"] == extent_id]
        extent_data = {}
        for d in json.loads(self.get_iscsi_extents()):
            extent_data[d["id"]] = d
        target_data = {}
        for d in json.loads(self.get_iscsi_targets()):
            target_data[d["id"]] = d
        tg_by_target = {}
        for d in json.loads(self.get_iscsi_targetgroup()):
            if d["iscsi_target"] in tg_by_target:
                tg_by_target[d["iscsi_target"]].append(d)
            else:
                tg_by_target[d["iscsi_target"]] = [d]
        ig_data = {}
        for d in json.loads(self.get_iscsi_authorizedinitiator()):
            ig_data[d["id"]] = d
        mappings = {}
        for d in tte_data:
            for tg in tg_by_target[d["iscsi_target"]]:
                ig_id = tg["iscsi_target_initiatorgroup"]
                ig = ig_data[ig_id]
                for hba_id in ig["iscsi_target_initiator_initiators"].split("\n"):
                    tgt_id = target_data[tg["iscsi_target"]]["iscsi_target_name"]
                    mappings[hba_id+":"+tgt_id] = {
                       "disk_id": extent_data[d["iscsi_extent"]]["iscsi_target_extent_naa"].replace("0x", ""),
                       "tgt_id": tgt_id,
                       "hba_id": hba_id,
                    }
        return mappings

    def resize_zvol(self, name=None, naa=None, size=None, **kwargs):
        if size is None:
            raise ex.excError("'size' key is mandatory")
        if name is None and naa is None:
            raise ex.excError("'name' or 'naa' must be specified")
        data = self.get_iscsi_extent(name=name, naa=naa)
        if data is None:
            raise ex.excError("extent not found")
        volume = self.extent_volume(data)
        if volume is None:
            raise ex.excError("volume not found")
        if size.startswith("+"):
            incr = convert_size(size.lstrip("+"), _to="MB")
            zvol_data = self.get_zvol(volume=volume, name=data["iscsi_target_extent_name"])
            current_size = convert_size(int(zvol_data["volsize"]), _to="MB")
            size = str(current_size + incr) + "MB"
        else:
            size = str(convert_size(size, _to="MB")) + "MB"

        d = {
            "volsize": size,
        }
        buff = self.put('/storage/volume/%s/zvols/%s' % (volume, data["iscsi_target_extent_name"]), d)
        try:
            return json.loads(buff)
        except ValueError:
            raise ex.excError(buff)

    def del_iscsi_initiatorgroup(self, ig_id=None, **kwargs):
        content = self.get_iscsi_authorizedinitiator_id(ig_id)
        try:
            data = json.loads(content)
        except ValueError:
            raise ex.excError("initiator group not found")
        self._del_iscsi_initiatorgroup(ig_id=ig_id, **kwargs)
        print(json.dumps(data, indent=8))
        return data

    def _del_iscsi_initiatorgroup(self, ig_id=None, **kwargs):
        if id is None:
            raise ex.excError("'id' in mandatory")
        response = self.delete('/services/iscsi/authorizedinitiator/%d' % ig_id)
        if response.status_code != 204:
            raise ex.excError(str(response))

    def add_iscsi_initiatorgroup(self, **kwargs):
        data = self._add_iscsi_initiatorgroup(**kwargs)
        print(json.dumps(data, indent=8))
        return data

    def _add_iscsi_initiatorgroup(self, initiators=None, auth_network="ALL", comment=None,
                                  **kwargs):
        for key in ["initiators"]:
            if locals()[key] is None:
                raise ex.excError("'%s' key is mandatory" % key)
        d = {
            "iscsi_target_initiator_initiators": ",".join(initiators),
            "iscsi_target_initiator_auth_network": auth_network,
        }
        if comment:
            d["iscsi_target_initiator_comment"] = comment

        buff = self.post('/services/iscsi/authorizedinitiator/', d)
        try:
            return json.loads(buff)
        except ValueError:
            raise ex.excError(buff)

    # targetgroup
    def del_iscsi_targetgroup(self, tg_id=None, **kwargs):
        content = self.get_iscsi_targetgroup_id(tg_id)
        try:
            data = json.loads(content)
        except ValueError:
            raise ex.excError("target group not found")
        self._del_iscsi_targetgroup(tg_id=tg_id, **kwargs)
        print(json.dumps(data, indent=8))
        return data

    def _del_iscsi_targetgroup(self, tg_id=None, **kwargs):
        if tg_id is None:
            raise ex.excError("'tg_id' is mandatory")
        response = self.delete('/services/iscsi/targetgroup/%d' % tg_id)
        if response.status_code != 204:
            raise ex.excError(str(response))

    def add_iscsi_targetgroup(self, **kwargs):
        data = self._add_iscsi_targetgroup(**kwargs)
        print(json.dumps(data, indent=8))
        return data

    def _add_iscsi_targetgroup(self, portal_id=None, initiatorgroup_id=None,
                               target_id=None, authtype="None",
                               authgroup_id=None, **kwargs):
        for key in ["portal_id", "initiatorgroup_id", "target_id"]:
            if locals()[key] is None:
                raise ex.excError("'%s' key is mandatory" % key)
        d = {
            "iscsi_target": target_id,
            "iscsi_target_initiatorgroup": initiatorgroup_id,
            "iscsi_target_portalgroup": portal_id,
            "iscsi_target_authtype": authtype,
            "iscsi_target_authgroup": -1,
            "iscsi_target_initialdigest": "Auto",
        }
        if authgroup_id:
            d["iscsi_target_authgroup"] = authgroup_id

        print(d)
        buff = self.post('/services/iscsi/targetgroup/', d)
        try:
            return json.loads(buff)
        except ValueError:
            raise ex.excError(buff)

    # target
    def del_iscsi_target(self, target_id=None, **kwargs):
        content = self.get_iscsi_target_id(target_id)
        try:
            data = json.loads(content)
        except ValueError:
            raise ex.excError("target not found")
        self._del_iscsi_target(target_id=target_id, **kwargs)
        print(json.dumps(data, indent=8))
        return data

    def _del_iscsi_target(self, target_id=None, **kwargs):
        if target_id is None:
            raise ex.excError("'target_id' is mandatory")
        response = self.delete('/services/iscsi/target/%d' % target_id)
        if response.status_code != 204:
            raise ex.excError(str(response))

    def add_iscsi_target(self, **kwargs):
        data = self._add_iscsi_target(**kwargs)
        print(json.dumps(data, indent=8))
        return data

    def _add_iscsi_target(self, name=None, alias=None, **kwargs):
        for key in ["name"]:
            if locals()[key] is None:
                raise ex.excError("'%s' key is mandatory" % key)
        d = {
            "iscsi_target_name": name,
        }
        if alias:
            d["iscsi_target_alias"] = alias

        buff = self.post('/services/iscsi/target/', d)
        try:
            return json.loads(buff)
        except ValueError:
            raise ex.excError(buff)

    def add_iscsi_file(self, name=None, size=None, volume=None, targets=None,
                       mappings=None, insecure_tpc=True, blocksize=512, **kwargs):
        for key in ["name", "size", "volume"]:
            if locals()[key] is None:
                raise ex.excError("'%s' key is mandatory" % key)

        if targets is None and mappings is None:
            raise ex.excError("'targets' or 'mappings' must be specified")

        if mappings is not None and targets is None:
            targets = self.translate_mappings(mappings)

        data = self.add_iscsi_file_extent(name=name, size=size, volume=volume, **kwargs)

        if "id" not in data:
            if "iscsi_target_extent_name" in data:
                if isinstance(data["iscsi_target_extent_name"], list):
                    raise ex.excError("\n".join(data["iscsi_target_extent_name"]))
                raise ex.excError(data["iscsi_target_extent_name"])
            raise ex.excError(str(data))
        self.add_iscsi_targets_to_extent(extent_id=data["id"], targets=targets, **kwargs)
        disk_id = data["iscsi_target_extent_naa"].replace("0x", "")
        results = {
            "driver_data": data,
            "disk_id": disk_id,
            "disk_devid": data["id"],
            "mappings": self.list_mappings(naa=disk_id),
        }
        return results

    def del_iscsi_file(self, name=None, naa=None, **kwargs):
        if name is None and naa is None:
            raise ex.excError("'name' or 'naa' must be specified")
        data = self.get_iscsi_extent(name=name, naa=naa)
        if data is None:
            return
        self.del_iscsi_extent(data["id"])
        print(json.dumps(data, indent=8))

    def translate_mappings(self, mappings):
        targets = set()
        for mapping in mappings:
            elements = mapping.split(":")
            targets |= set(elements[-1].split(","))
        targets = list(targets)
        return targets

    def add_iscsi_zvol(self, name=None, size=None, volume=None, targets=None,
                       mappings=None, insecure_tpc=True, blocksize=512, **kwargs):
        for key in ["name", "size", "volume"]:
            if locals()[key] is None:
                raise ex.excError("'%s' key is mandatory" % key)
        if targets is None and mappings is None:
            raise ex.excError("'targets' or 'mappings' must be specified")

        if mappings is not None and targets is None:
            targets = self.translate_mappings(mappings)

        data = self.add_iscsi_zvol_extent(name=name, size=size, volume=volume, **kwargs)

        if "id" not in data:
            if "iscsi_target_extent_name" in data:
                if isinstance(data["iscsi_target_extent_name"], list):
                    raise ex.excError("\n".join(data["iscsi_target_extent_name"]))
                raise ex.excError(data["iscsi_target_extent_name"])
            raise ex.excError(str(data))
        self.add_iscsi_targets_to_extent(extent_id=data["id"], targets=targets, **kwargs)
        self.add_diskinfo(data, size, volume)
        disk_id = data["iscsi_target_extent_naa"].replace("0x", "")
        results = {
            "driver_data": data,
            "disk_id": disk_id,
            "disk_devid": data["id"],
            "mappings": self.list_mappings(naa=disk_id),
        }
        return results

    def del_iscsi_zvol(self, name=None, naa=None, **kwargs):
        if name is None and naa is None:
            raise ex.excError("'name' or 'naa' must be specified")
        data = self.get_iscsi_extent(name=name, naa=naa)
        if data is None:
            return
        volume = self.extent_volume(data)
        self.del_iscsi_extent(data["id"])
        self.del_zvol(name=name, volume=volume)
        self.del_diskinfo(data["iscsi_target_extent_naa"].replace("0x", ""))
        print(json.dumps(data, indent=8))

    def extent_volume(self, data):
        path = data["iscsi_target_extent_path"].split("/")
        volume = path[path.index("zvol")+1]
        return volume

    def list_volume(self, **kwargs):
        data = json.loads(self.get_volumes())
        print(json.dumps(data, indent=8))

    def list_iscsi_target(self, **kwargs):
        data = json.loads(self.get_iscsi_targets())
        print(json.dumps(data, indent=8))

    def list_iscsi_targettoextent(self, **kwargs):
        data = json.loads(self.get_iscsi_targettoextents())
        print(json.dumps(data, indent=8))

    def list_iscsi_portal(self, **kwargs):
        data = json.loads(self.get_iscsi_portal())
        print(json.dumps(data, indent=8))

    def list_iscsi_targetgroup(self, **kwargs):
        data = json.loads(self.get_iscsi_targetgroup())
        print(json.dumps(data, indent=8))

    def list_iscsi_extent(self, **kwargs):
        data = json.loads(self.get_iscsi_extents())
        print(json.dumps(data, indent=8))

    def list_iscsi_initiatorgroup(self, **kwargs):
        data = json.loads(self.get_iscsi_authorizedinitiator())
        print(json.dumps(data, indent=8))

    def del_diskinfo(self, disk_id):
        if disk_id in (None, ""):
            return
        if self.node is None:
            return
        try:
            result = self.node.collector_rest_delete("/disks/%s" % disk_id)
        except Exception as exc:
            raise ex.excError(str(exc))
        if "error" in result:
            raise ex.excError(result["error"])
        return result

    def add_diskinfo(self, data, size=None, volume=None):
        if self.node is None:
            return
        try:
            result = self.node.collector_rest_post("/disks", {
                "disk_id": data["iscsi_target_extent_naa"].replace("0x", ""),
                "disk_devid": data["id"],
                "disk_name": data["iscsi_target_extent_name"],
                "disk_size": convert_size(size, _to="MB"),
                "disk_alloc": 0,
                "disk_arrayid": self.name,
                "disk_group": volume,
            })
        except Exception as exc:
            raise ex.excError(str(exc))
        if "error" in data:
            raise ex.excError(result["error"])
        return result

def do_action(action, array_name=None, node=None, **kwargs):
    o = Freenass()
    array = o.get_freenas(array_name)
    if array is None:
        raise ex.excError("array %s not found" % array_name)
    array.node = node
    if not hasattr(array, action):
        raise ex.excError("not implemented")
    result = getattr(array, action)(**kwargs)
    if result is not None:
        print(json.dumps(result, indent=4))

def main(argv, node=None):
    parser = OptParser(prog=PROG, options=OPT, actions=ACTIONS,
                       deprecated_actions=DEPRECATED_ACTIONS,
                       global_options=GLOBAL_OPTS)
    options, action = parser.parse_args(argv)
    kwargs = vars(options)
    do_action(action, node=node, **kwargs)

if __name__ == "__main__":
    try:
        main(sys.argv)
        ret = 0
    except ex.excError as exc:
        print(exc, file=sys.stderr)
        ret = 1
    sys.exit(ret)


0707010005c939000081b40000000000000000000000015ab2175400000309000000660000000000000000000000000000002400000000root/usr/share/opensvc/lib/rcGce.py   from rcUtilities import is_string, justcall
import json

class Gce(object):
    valid_auth = False

    def gce_auth(self):
        cmd = ["gcloud", "auth", "list", "--format", "json"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return False
        self.log.debug(out)
        data = json.loads(out)
        if "active_account" not in data:
            return False
        if not is_string(data["active_account"]):
            return False
        if len(data["active_account"]) == 0:
            return False
        return True
        
    def wait_gce_auth(self):
        if self.valid_auth:
            return
        self.wait_for_fn(self.gce_auth, 120, 1, errmsg="waited 120 seconds for a valid gcloud auth")
        self.valid_auth = True

   0707010005c93a000081b40000000000000000000000015ab21754000004e6000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/rcGceDisks.py  import rcExceptions as ex
import json
from rcUtilities import justcall
from rcGlobalEnv import rcEnv

class GceDiskss(object):
    arrays = []

    def __init__(self, objects=[]):
        self.arrays.append(GceDisks())

    def __iter__(self):
        for array in self.arrays:
            yield(array)

class GceDisks(object):
    def __init__(self):
        self.keys = ['disks', 'snapshots', 'quotas', 'instances']
        self.name = "gce project "+rcEnv.fqdn.split(".")[-2]

    def get_disks(self):
        cmd = ["gcloud", "compute", "disks", "list", "-q", "--format", "json"]
        out, err, ret = justcall(cmd)
        return out

    def get_snapshots(self):
        cmd = ["gcloud", "compute", "snapshots", "list", "-q", "--format", "json"]
        out, err, ret = justcall(cmd)
        return out

    def get_quotas(self):
        cmd = ["gcloud", "compute", "regions", "list", "-q", "--format", "json"]
        out, err, ret = justcall(cmd)
        return out

    def get_instances(self):
        cmd = ["gcloud", "compute", "instances", "list", "-q", "--format", "json"]
        out, err, ret = justcall(cmd)
        return out


if __name__ == "__main__":
    o = GceDiskss()
    for gcedisks in o:
        print(gcedisks.get_disks())
  0707010005c93b000081b40000000000000000000000015ab2175400001d2f000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/rcGlobalEnv.py """module rcGlobalEnv module define rcEnv class
   rcEnv class attribute may be updated with rcLocalEnv module if present
   rcLocalEnv module is not provided with opensvc and allow customers to
   redefine following vars:
       o dbopensvc_host
       o dbopensvc_port
       o rsh
       o rcp
   rcLocalEnv.py may be installed into path_opensvc/lib
"""
import sys
import os
import platform
import socket
import uuid
import time

class Storage(dict):
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__
    __getitem__ = dict.get
    __getattr__ = dict.get
    __repr__ = lambda self: '<Storage %s>' % dict.__repr__(self)
    __getstate__ = lambda self: None
    __copy__ = lambda self: Storage(self)

def get_osvc_paths(osvc_root_path=None, sysname=None, detect=False):
    o = Storage()

    if osvc_root_path:
        o.pathsvc = osvc_root_path
    elif detect:
        o.pathsvc = os.path.realpath(os.path.join(os.path.dirname(__file__), ".."))
    else:
        o.pathsvc = '/usr/share/opensvc'

    if o.pathsvc == '/usr/share/opensvc':
        o.pathlib = '/usr/share/opensvc/lib'
        o.pathbin = '/usr/bin'
        o.pathetc = '/etc/opensvc'
        o.pathlog = '/var/log/opensvc'
        o.pathtmp = '/var/tmp/opensvc'
        o.pathvar = '/var/lib/opensvc'
        o.pathdoc = '/usr/share/doc/opensvc'
        o.pathlock = '/var/lib/opensvc/lock'
        o.pathcron = '/usr/share/opensvc'
        o.postinstall = '/usr/share/opensvc/bin/postinstall'
        o.preinstall = '/usr/share/opensvc/bin/preinstall'
    else:
        o.pathlib = os.path.join(o.pathsvc, 'lib')
        o.pathbin = os.path.join(o.pathsvc, 'bin')
        o.pathetc = os.path.join(o.pathsvc, 'etc')
        o.pathlog = os.path.join(o.pathsvc, 'log')
        o.pathtmp = os.path.join(o.pathsvc, 'tmp')
        o.pathvar = os.path.join(o.pathsvc, 'var')
        o.pathdoc = os.path.join(o.pathsvc, 'usr', 'share', 'doc')
        o.pathlock = os.path.join(o.pathvar, 'lock')
        o.pathcron = o.pathbin
        o.postinstall = os.path.join(o.pathbin, 'postinstall')
        o.preinstall = os.path.join(o.pathbin, 'preinstall')

    if os.name == "nt":
        o.svcmgr = os.path.join(o.pathsvc, "svcmgr.cmd")
        o.nodemgr = os.path.join(o.pathsvc, "nodemgr.cmd")
        o.svcmon = os.path.join(o.pathsvc, "svcmon.cmd")
        o.cron = os.path.join(o.pathsvc, "cron.cmd")
    else:
        o.svcmgr = os.path.join(o.pathbin, "svcmgr")
        o.nodemgr = os.path.join(o.pathbin, "nodemgr")
        o.svcmon = os.path.join(o.pathbin, "svcmon")
        o.cron = os.path.join(o.pathcron, "cron")

    o.nodeconf = os.path.join(o.pathetc, "node.conf")
    o.authconf = os.path.join(o.pathetc, "auth.conf")

    o.lsnruxsockd = os.path.join(o.pathvar, "lsnr")
    o.lsnruxsock = os.path.join(o.lsnruxsockd, "lsnr.sock")
    o.dnsuxsockd = os.path.join(o.pathvar, "dns")
    o.dnsuxsock = os.path.join(o.dnsuxsockd, "pdns.sock")
    o.pathcomp = os.path.join(o.pathvar, "compliance")
    o.pathcomposvc = os.path.join(o.pathcomp, "com.opensvc")
    o.safe = os.path.join(o.pathvar, "safe")
    o.drp_path = os.path.join(o.pathvar, "cache")

    o.daemon_pid = os.path.join(o.pathvar, "osvcd.pid")
    o.daemon_lock = os.path.join(o.pathlock, "osvcd.lock")

    return o

def create_or_update_dir(d):
    if not os.path.exists(d):
        os.makedirs(d)
    else:
        # update tmpdir timestamp to avoid tmpwatch kicking-in while we run
        now = time.time()
        try:
            os.utime(d, (now, now))
        except:
            # unprivileged
            pass

class rcEnv:
    """Class to store globals
    """
    session_uuid = str(uuid.uuid4())
    initial_env = os.environ.copy()
    os.environ["OSVC_SESSION_UUID"] = session_uuid
    node_env = "TST"

    allowed_svc_envs = [
        'DEV',
        'DRP',
        'FOR',
        'INT',
        'PRA',
        'PRD',
        'PRJ',
        'PPRD',
        'REC',
        'STG',
        'TMP',
        'TST',
        'UAT',
    ]
    fs_pooling = [
        "zfs",
    ]
    fs_non_pooling = [
        "ext2", "ext3", "ext4", "xfs", "btrfs", "vfat",
        "reiserfs", "jfs", "jfs2", "bfs", "msdos", "ufs",
        "ufs2", "minix", "xia", "ext", "umsdos", "hpfs",
        "ntfs", "reiserfs4", "vxfs", "hfs", "hfsplus",
        "qnx4", "ocfs", "ocfs2", "nilfs", "jffs", "jffs2",
        "tux3", "f2fs", "logfs", "gfs", "gfs2", "gpfs",
    ]
    _platform = sys.platform
    sysname, x, x, x, machine, x = platform.uname()
    nodename = socket.gethostname().lower()
    fqdn = socket.getfqdn().lower()
    listener_port = 1214

    """program used to execute remote command on other nodes or virtual hosts
    """
    if _platform == "sunos5" :
        if os.path.exists('/usr/local/bin/ssh'):
            rsh = "/usr/local/bin/ssh -q -o StrictHostKeyChecking=no -o ForwardX11=no -o BatchMode=yes -o ConnectTimeout=10"
            rcp = "/usr/local/bin/scp -q -o StrictHostKeyChecking=no -o ForwardX11=no -o BatchMode=yes -o ConnectTimeout=10"
        else:
            rsh = "/usr/bin/ssh -q -o StrictHostKeyChecking=no -o ForwardX11=no -o BatchMode=yes -n"
            rcp = "/usr/bin/scp -q -o StrictHostKeyChecking=no -o ForwardX11=no -o BatchMode=yes"
    elif os.path.exists('/etc/vmware-release'):
        rsh = "/usr/bin/ssh -q -o StrictHostKeyChecking=no -o ForwardX11=no -o BatchMode=yes"
        rcp = "/usr/bin/scp -q -o StrictHostKeyChecking=no -o ForwardX11=no -o BatchMode=yes"
    elif sysname == 'OSF1':
        rsh = "ssh -o StrictHostKeyChecking=no -o ForwardX11=no -o BatchMode=yes -o ConnectTimeout=10"
        rcp = "scp -o StrictHostKeyChecking=no -o ForwardX11=no -o BatchMode=yes -o ConnectTimeout=10"
    else:
        rsh = "/usr/bin/ssh -q -o StrictHostKeyChecking=no -o ForwardX11=no -o BatchMode=yes -o ConnectTimeout=10"
        rcp = "/usr/bin/scp -q -o StrictHostKeyChecking=no -o ForwardX11=no -o BatchMode=yes -o ConnectTimeout=10"

    vt_cloud = ['vcloud', 'openstack', 'amazon']
    vt_libvirt = ['kvm']
    vt_vm = ['ldom', 'hpvm', 'kvm', 'xen', 'vbox', 'ovm', 'esx'] + vt_cloud
    vt_container = ['zone', 'lxd', 'lxc', 'jail', 'vz', 'srp', 'docker']
    vt_supported = vt_vm + vt_container

    dbopensvc = None
    dbopensvc_host = None
    dbcompliance = None
    dbcompliance_host = None
    paths = get_osvc_paths(sysname=sysname, detect=True)

    syspaths = Storage(
        df="/bin/df",
        mount="/bin/mount",
        umount="/bin/umount",
        zfs="/sbin/zfs",
        zpool="/sbin/zpool",
    )
    if sysname == "Linux":
        syspaths.blkid = "/sbin/blkid"
        syspaths.dmsetup = "/sbin/dmsetup"
        syspaths.ip = "/sbin/ip"
        syspaths.losetup = "/sbin/losetup"
        syspaths.lsmod = "/sbin/lsmod"
        syspaths.lvs = "/sbin/lvs"
        syspaths.multipath = "/sbin/multipath"
        syspaths.multipathd = "/sbin/multipathd"
        syspaths.nsenter = "/usr/bin/nsenter"
        syspaths.pvs = "/sbin/pvs"
        syspaths.pvscan = "/sbin/pvscan"
        syspaths.vgs = "/sbin/vgs"
    elif sysname == "SunOS":
        syspaths.df = "/usr/sbin/df"
        syspaths.mount = "/usr/sbin/mount"
        syspaths.umount = "/usr/sbin/umount"
        syspaths.zfs = "/usr/sbin/zfs"
        syspaths.zpool = "/usr/sbin/zpool"
    elif sysname == "AIX":
        syspaths.df = "/usr/sbin/df"
        syspaths.mount = "/usr/sbin/mount"
        syspaths.umount = "/usr/sbin/umount"

    create_or_update_dir(paths.pathtmp)
 0707010005c93c000081b40000000000000000000000015ab2175400006405000000660000000000000000000000000000002400000000root/usr/share/opensvc/lib/rcHds.py   from __future__ import print_function

import logging
import os
import rcConfigParser
import sys
import json
from xml.etree.ElementTree import XML, fromstring

import rcExceptions as ex
from rcGlobalEnv import rcEnv, Storage
from rcUtilities import which, justcall, lazy
from converters import convert_size
from rcOptParser import OptParser
from optparse import Option

PROG = "nodemgr array"
OPT = Storage({
    "help": Option(
        "-h", "--help", action="store_true", dest="parm_help",
        help="show this help message and exit"),
    "array": Option(
        "-a", "--array", action="store", dest="array_name",
        help="The name of the array, as defined in auth.conf"),
    "pool": Option(
        "--pool", action="store", dest="pool",
        help="The name of the DP pool"),
    "size": Option(
        "--size", action="store", dest="size",
        help="The disk size, expressed as a size expression like 1g, 100mib, ..."),
    "lun": Option(
        "--lun", action="store", dest="lun",
        help="The LUN ID to assign on LU mapping"),
    "mappings": Option(
        "--mappings", action="append", dest="mappings",
        help="A <hba_id>:<tgt_id>,<tgt_id>,... mapping used in add map in replacement of --targetgroup and --initiatorgroup. Can be specified multiple times."),
    "name": Option(
        "--name", action="store", dest="name",
        help="A logical unit label"),
    "devnum": Option(
        "--devnum", action="store", dest="devnum",
        help="A XX:CU:LDEV logical unit name"),
})

GLOBAL_OPTS = [
    OPT.array,
]

DEPRECATED_ACTIONS = []

ACTIONS = {
    "Generic actions": {
        "add_disk": {
            "msg": "Add and present a disk",
            "options": [
                OPT.name,
                OPT.size,
                OPT.pool,
                OPT.mappings,
            ],
        },
        "add_map": {
            "msg": "Present a disk",
            "options": [
                OPT.devnum,
                OPT.mappings,
                OPT.lun,
            ],
        },
        "del_disk": {
            "msg": "Delete a disk",
            "options": [
                OPT.devnum,
            ],
        },
        "del_map": {
            "msg": "Unpresent a disk",
            "options": [
                OPT.devnum,
                OPT.mappings,
            ],
        },
        "rename_disk": {
            "msg": "Rename a disk",
            "options": [
                OPT.devnum,
                OPT.name,
            ],
        },
        "resize_disk": {
            "msg": "Resize a disk",
            "options": [
                OPT.devnum,
                OPT.size,
            ],
        },
    },
    "Low-level actions": {
        "list_arrays": {
            "msg": "List arrays",
        },
        "list_pools": {
            "msg": "List pools",
        },
        "list_arraygroups": {
            "msg": "List array groups",
        },
        "list_domains": {
            "msg": "List host groups",
        },
        "list_ports": {
            "msg": "List ports",
        },
        "list_logicalunits": {
            "msg": "List logical units",
            "options": [
                OPT.devnum,
            ],
        },
    },
}

class Arrays(object):
    arrays = []
    def __init__(self, objects=[]):
        self.objects = objects
        if len(objects) > 0:
            self.filtering = True
        else:
            self.filtering = False

        cf = rcEnv.paths.authconf
        if not os.path.exists(cf):
            return
        conf = rcConfigParser.RawConfigParser()
        conf.read(cf)
        m = []
        for s in conf.sections():
            try:
                stype = conf.get(s, 'type')
            except:
                continue
            if stype != "hds":
                continue
            try:
                bin = conf.get(s, 'bin')
            except:
                bin = None
            try:
                jre_path = conf.get(s, 'jre_path')
                os.environ["HDVM_CLI_JRE_PATH"] = jre_path
            except:
                pass
            try:
                url = conf.get(s, 'url')
                arrays = conf.get(s, 'array').split()
                username = conf.get(s, 'username')
                password = conf.get(s, 'password')
                m += [(url, arrays, username, password, bin)]
            except:
                print("error parsing section", s)
                pass

        del(conf)
        done = []
        for url, arrays, username, password, bin in m:
            for name in arrays:
                if self.filtering and name not in self.objects:
                    continue
                if name in done:
                    continue
                self.arrays.append(Array(name, url, username, password, bin))
                done.append(name)

    def __iter__(self):
        for array in self.arrays:
            yield(array)

    def get_array(self, name):
        for array in self.arrays:
            if array.name == name:
                return array
        return None

class Array(object):
    def __init__(self, name, url, username, password, bin=None):
        self.keys = ['array', 'lu', 'arraygroup', 'port', 'pool']
        self.name = name
        self.model = name.split(".")[0]
        self.serial = name.split(".")[-1]
        self.url = url
        self.username = username
        self.password = password
        if bin is None:
            self.bin = "HiCommandCLI"
        else:
            self.bin = bin
        self.domain_portname = {}
        self.port_portname = {}
        self.log = logging.getLogger(rcEnv.nodename+".array.sym."+self.name)

    def cmd(self, cmd, scoped=True, xml=True, log=False):
        if which(self.bin) is None:
            raise ex.excError("Can not find %s"%self.bin)
        l = [
            self.bin, self.url, cmd[0],
            "-u", self.username,
            "-p", self.password,
        ]
        if xml:
            l += [
                "-f", "xml",
            ]
        if scoped:
            l += [
                "serialnum="+self.serial,
                "model="+self.model,
            ]
        if len(cmd) > 1:
            l += cmd[1:]
        if log:
            _l = [] + l
            _l[6] = "xxxx"
            self.log.info(" ".join(_l))
        out, err, ret = justcall(l)
        if ret != 0:
            self.log.error(err)
            raise ex.excError(err)
        return out, err, ret

    def parse(self, out):
        lines = out.splitlines()

        if lines[0] == "RESPONSE:":
            # discard the "RESPONSE:" first line
            lines = lines[1:]

        def get_key_val(line):
            idx = line.index("=")
            key = line[:idx].strip()
            val = line[idx+1:].strip()
            try:
                val = int(val.replace(" ", "").replace(",", ""))
            except ValueError:
                pass
            return key, val

        def _parse_instance(lines, start, ref_indent):
            #print("parse instance", start, lines[start-1])
            data = {}
            nidx = -1
            for idx, line in enumerate(lines[start:]):
                if nidx > 0 and start+idx < nidx:
                    continue
                indent = len(line) - len(line.lstrip())
                if indent < ref_indent:
                    return data, start+idx
                if line.strip().startswith("List of "):
                    obj_type = line.strip().split()[3]
                    data[obj_type], nidx = _parse_list(lines, start+idx+1)
                try:
                    key, val = get_key_val(line)
                    data[key] = val
                except ValueError:
                    pass
            return data, start+idx

        def _parse_list(lines, start=0):
            #if start > 0:
            #    print("parse list    ", start, lines[start-1])
            data = []
            nidx = -1
            ref_indent = len(lines[start]) - len(lines[start].lstrip())
            marker = lines[start]
            for idx, line in enumerate(lines[start:]):
                if nidx > 0 and start+idx < nidx:
                    continue
                indent = len(line) - len(line.lstrip())
                if indent < ref_indent:
                    return data, start+idx
                if indent > ref_indent:
                    continue
                if line == marker:
                    instance, nidx = _parse_instance(lines, start+idx+1, indent)
                    data.append(instance)
            return data, start+idx

        data, nidx =_parse_list(lines)
        return data

    def get_array_data(self, scoped=True):
        cmd = ['GetStorageArray']
        out, err, ret = self.cmd(cmd, scoped=scoped)
        tree = fromstring(out)
        data = []
        for elem in tree.getiterator("StorageArray"):
            data.append(elem.attrib)
        return data

    def get_array(self):
        return json.dumps(self.get_array_data(scoped=True), indent=4)

    def get_lu_data(self, devnum=None):
        cmd = ['GetStorageArray', 'subtarget=Logicalunit', 'lusubinfo=Path,LDEV,VolumeConnection']
        if devnum:
            cmd += ["displayname="+str(devnum)]
        out, err, ret = self.cmd(cmd)
        tree = fromstring(out)
        data = []
        for e_lu in tree.getiterator("LogicalUnit"):
            lu = e_lu.attrib
            lu["Path"] = []
            for e_path in e_lu.getiterator("Path"):
                lu["Path"].append(e_path.attrib)
            for e_ldev in e_lu.getiterator("LDEV"):
                ldev = e_ldev.attrib
                for e_label in e_ldev.getiterator("ObjectLabel"):
                    lu["label"] = e_label.attrib["label"]
            data.append(lu)
        return data

    def get_lu(self):
        return json.dumps(self.get_lu_data(), indent=4)

    @lazy
    def lu_data(self):
        return self.get_lu_data()

    def to_devnum(self, devnum):
        devnum = str(devnum)
        if ":" in devnum:
            # 00:00:00 or 00:00 format
            devnum = devnum.replace(":", "")
            return str(int(devnum, 16))
        if "." in devnum:
            # <serial>.<culd> format (collector inventory fmt)
            devnum = devnum.split(".")[-1]
            return str(int(devnum, 16))
        if len(devnum) in (32, 33):
            # wwid format
            devnum = devnum[-4:]
            return str(int(devnum, 16))

        return devnum

    def get_logicalunit(self, devnum=None):
        if devnum is None:
            return
        for lu in self.lu_data:
            if lu["devNum"] == devnum:
                return lu

    def get_pool_data(self):
        cmd = ['GetStorageArray', 'subtarget=Pool']
        out, err, ret = self.cmd(cmd)
        tree = fromstring(out)
        data = []
        for elem in tree.getiterator("Pool"):
            data.append(elem.attrib)
        return data

    @lazy
    def pool_data(self):
        return self.get_pool_data()

    def get_pool(self):
        return json.dumps(self.get_pool_data(), indent=4)

    def get_arraygroup_data(self):
        cmd = ['GetStorageArray', 'subtarget=ArrayGroup']
        out, err, ret = self.cmd(cmd)
        tree = fromstring(out)
        data = []
        for elem in tree.getiterator("ArrayGroup"):
            data.append(elem.attrib)
        return data

    def get_arraygroup(self):
        return json.dumps(self.get_arraygroup_data(), indent=4)

    def get_port_data(self):
        cmd = ['GetStorageArray', 'subtarget=Port']
        out, err, ret = self.cmd(cmd)
        tree = fromstring(out)
        data = []
        for elem in tree.getiterator("Port"):
            port = elem.attrib
            port["worldWidePortName"] = port["worldWidePortName"].replace(".", "").lower()
            data.append(port)
        return data

    @lazy
    def port_data(self):
        return self.get_port_data()

    def get_port(self):
        return json.dumps(self.get_port_data(), indent=4)

    def get_domain_data(self):
        cmd = ['GetStorageArray', 'subtarget=HostStorageDomain', 'hsdsubinfo=WWN,Path']
        out, err, ret = self.cmd(cmd)
        tree = fromstring(out)
        data = []
        for elem in tree.getiterator("HostStorageDomain"):
            d = elem.attrib
            d["WWN"] = []
            d["Path"] = []
            for subelem in elem.getiterator("WWN"):
                wwn = subelem.attrib
                wwn["WWN"] = wwn["WWN"].replace(".", "").lower()
                d["WWN"].append(wwn)
            for subelem in elem.getiterator("Path"):
                path = subelem.attrib
                d["Path"].append(path)
            data.append(d)
        return data

    @lazy
    def domain_data(self):
        return self.get_domain_data()

    def get_target_port(self, target):
        for port in self.port_data:
            if target == port["worldWidePortName"]:
                return port

    def get_domain_used_lun_ids(self, domain):
        return [int(path["LUN"]) for path in domain["Path"]]

    def get_free_lun_id(self, used_lun_ids):
        for lun_id in range(65536):
            if lun_id not in used_lun_ids:
                return lun_id

    def get_domains(self, hba_id, tgt_id):
        domains = []
        port = self.get_target_port(tgt_id)
        if port is None:
            return domains
        for domain in self.domain_data:
            for wwn in domain["WWN"]:
                if hba_id == wwn["WWN"] and domain["portName"] == port["displayName"]:
                    domains.append(domain)
        return domains

    def get_pool_by_name(self, poolname):
        for pool in self.pool_data:
            if pool["name"] == poolname:
                return pool

    def get_pool_by_id(self, pool_id):
        for pool in self.pool_data:
            if pool["poolID"] == pool_id:
                return pool

    def list_array(self, **kwargs):
        data = self.get_array_data()
        print(json.dumps(data, indent=4))

    def list_arrays(self, **kwargs):
        data = self.get_array_data(scoped=False)
        print(json.dumps(data, indent=4))

    def list_pools(self, **kwargs):
        data = self.get_pool_data()
        print(json.dumps(data, indent=4))

    def list_arraygroups(self, **kwargs):
        data = self.get_arraygroup_data()
        print(json.dumps(data, indent=4))

    def list_ports(self, **kwargs):
        data = self.get_port_data()
        print(json.dumps(data, indent=4))

    def list_logicalunits(self, devnum=None, **kwargs):
        data = self.get_lu_data(devnum=devnum)
        print(json.dumps(data, indent=4))

    def list_domains(self, **kwargs):
        data = self.get_domain_data()
        print(json.dumps(data, indent=4))

    def translate_mappings(self, mappings):
        internal_mappings = []
        used_lun_ids = set()
        for mapping in mappings:
            elements = mapping.split(":")
            hba_id = elements[0]
            targets = elements[-1].split(",")
            for tgt_id in targets:
                for domain_data in self.get_domains(hba_id, tgt_id):
                    used_lun_ids |= set(self.get_domain_used_lun_ids(domain_data))
                    domain = domain_data["domainID"]
                    portname = domain_data["portName"]
                    _mapping = {
                        "domain": domain_data["domainID"],
                        "portname": domain_data["portName"],
                    }
                    if _mapping in internal_mappings:
                        continue
                    internal_mappings.append(_mapping)
        lun = self.get_free_lun_id(used_lun_ids)
        for idx, mapping in enumerate(internal_mappings):
            internal_mappings[idx]["lun"] = lun
        return internal_mappings

    def del_map(self, devnum=None, mappings=None, **kwargs):
        if devnum is None:
            raise ex.excError("--devnum is mandatory")
        devnum = self.to_devnum(devnum)
        results = []
        if mappings is not None:
            internal_mappings = self.translate_mappings(mappings)
        else:
            internal_mappings = []
            for dom in self.domain_data:
                for path in dom["Path"]:
                    if devnum == path["devNum"]:
                        mapping = {
                            "domain": path["domainID"],
                            "portname": path["portName"],
                        }
                        if mapping not in internal_mappings:
                            internal_mappings.append(mapping)

        for mapping in internal_mappings:
            result = self._del_map(devnum=devnum, domain=mapping["domain"],
                                   portname=mapping["portname"], **kwargs)
            if result is not None:
                results.append(result)
        if len(results) > 0:
            return results

    def _del_map(self, devnum=None, domain=None, portname=None, **kwargs):
        if devnum is None:
            raise ex.excError("--devnum is mandatory")
        if domain is None:
            raise ex.excError("--domain is mandatory")
        if portname is None:
            raise ex.excError("--portname is mandatory")
        cmd = [
            "deletelun",
            "devnum="+str(devnum),
            "portname="+portname,
            "domain="+domain,
        ]
        out, err, ret = self.cmd(cmd, xml=False, log=True)
        if ret != 0:
            raise ex.excError(err)

    def add_map(self, devnum=None, mappings=None, lun=None, **kwargs):
        if devnum is None:
            raise ex.excError("--devnum is mandatory")
        devnum = self.to_devnum(devnum)
        if mappings is None:
            raise ex.excError("--mappings is mandatory")
        results = []
        if mappings is not None:
            internal_mappings = self.translate_mappings(mappings)
            for mapping in internal_mappings:
                if lun is None:
                    lun = mapping["lun"]
                result = self._add_map(devnum=devnum, domain=mapping["domain"],
                                       portname=mapping["portname"], lun=lun,
                                       **kwargs)
                if result is not None:
                    results.append(result)
        if len(results) > 0:
            return results

    def _add_map(self, devnum=None, domain=None, portname=None, lun=None, **kwargs):
        if devnum is None:
            raise ex.excError("--devnum is mandatory")
        if domain is None:
            raise ex.excError("--domain is mandatory")
        if portname is None:
            raise ex.excError("--portname is mandatory")
        if lun is None:
            raise ex.excError("--lun is mandatory")
        domain = str(domain)
        devnum = str(devnum)
        for dom in self.domain_data:
            for path in dom["Path"]:
                if portname == path["portName"] and \
                   domain == path["domainID"] and \
                   devnum == path["devNum"]:
                    print("Device %s is already mapped to port %s in domain %s" % (devnum, portname, domain))
                    return
        cmd = [
            "addlun",
            "devnum="+str(devnum),
            "portname="+portname,
            "domain="+domain,
            "lun="+str(lun),
        ]
        out, err, ret = self.cmd(cmd, xml=False, log=True)
        if ret != 0:
            raise ex.excError(err)
        data = self.parse(out)
        return data[0]["Path"][0]

    def add_disk(self, name=None, pool=None, size=None, lun=None, mappings=None, **kwargs):
        if pool is None:
            raise ex.excError("--pool is mandatory")
        if size == 0 or size is None:
            raise ex.excError("--size is mandatory")
        pool_id = self.get_pool_by_name(pool)["poolID"]
        cmd = [
            "addvirtualvolume",
            "capacity="+str(convert_size(size, _to="KB")),
            "capacitytype=KB",
            "poolid="+str(pool_id),
        ]
        out, err, ret = self.cmd(cmd, xml=False, log=True)
        if ret != 0:
            raise ex.excError(err)
        data = self.parse(out)
        ret = data[0]["ArrayGroup"][0]["Lu"][0]

        if name:
            self.rename_disk(devnum=ret["devNum"], name=name)
        if mappings:
            self.add_map(name=name, devnum=ret["devNum"], lun=lun, mappings=mappings)
        lun_data = self.get_lu_data(devnum=ret["displayName"])[0]
        self.push_diskinfo(lun_data, name, size)
        mappings = {}
        for path in lun_data["Path"]:
            domain = path["domainID"]
            port = path["portName"]
            if domain not in self.domain_portname:
                continue
            if port not in self.port_portname:
                continue
            for hba_id in self.domain_portname[domain]:
                for tgt_id in self.port_portname[port]:
                    mappings[hba_id+":"+tgt_id] = {
                        "hba_id": hba_id,
                        "tgt_id": tgt_id,
                        "lun": int(path["LUN"]),
                    }
        results = {
            "disk_id": ".".join(lun_data["objectID"].split(".")[-2:]),
            "disk_devid": lun_data["displayName"],
            "mappings": mappings,
            "driver_data": {
                 "lu": lun_data,
            },
        }
        return results

    def resize_disk(self, devnum=None, size=None, **kwargs):
        if devnum is None:
            raise ex.excError("--devnum is mandatory")
        devnum = self.to_devnum(devnum)
        if size == 0 or size is None:
            raise ex.excError("--size is mandatory")
        if size.startswith("+"):
            incr = convert_size(size.lstrip("+"), _to="KB")
            data = self.get_logicalunit(devnum=devnum)
            current_size = int(data["capacityInKB"])
            size = str(current_size + incr)
        else:
            size = str(convert_size(size, _to="KB"))
        cmd = [
            "modifyvirtualvolume",
            "capacity="+size,
            "capacitytype=KB",
            "devnums="+str(devnum),
        ]
        out, err, ret = self.cmd(cmd, xml=False, log=True)
        if ret != 0:
            raise ex.excError(err)

    def del_disk(self, devnum=None, **kwargs):
        if devnum is None:
            raise ex.excError("--devnum is mandatory")
        devnum = self.to_devnum(devnum)
        self.del_map(devnum=devnum)
        cmd = [
            "deletevirtualvolume",
            "devnums="+str(devnum),
        ]
        out, err, ret = self.cmd(cmd, xml=False, log=True)
        if ret != 0:
            raise ex.excError(err)
        self.del_diskinfo(devnum)

    def rename_disk(self, devnum=None, name=None, **kwargs):
        if devnum is None:
            raise ex.excError("--devnum is mandatory")
        if name is None:
            raise ex.excError("--name is mandatory")
        devnum = self.to_devnum(devnum)
        cmd = [
            "modifylabel",
            "devnums="+str(devnum),
            "label="+str(name),
        ]
        out, err, ret = self.cmd(cmd, xml=False, log=True)
        if ret != 0:
            raise ex.excError(err)
        data = self.parse(out)
        return data[0]

    def del_diskinfo(self, disk_id):
        if disk_id in (None, ""):
            return
        if self.node is None:
            return
        try:
            ret = self.node.collector_rest_delete("/disks/%s" % disk_id)
        except Exception as exc:
            raise ex.excError(str(exc))
        if "error" in ret:
            self.log.error("failed to delete the disk object in the collector: %s", ret["error"])
        return ret

    def push_diskinfo(self, data, name, size):
        if self.node is None:
            return
        try:
            ret = self.node.collector_rest_post("/disks", {
                "disk_id": self.serial+"."+str(data["devNum"]),
                "disk_devid": data["devNum"],
                "disk_name": str(name),
                "disk_size": convert_size(size, _to="MB"),
                "disk_alloc": 0,
                "disk_arrayid": self.name,
                "disk_group": self.get_pool_by_id(data["dpPoolID"]),
            })
        except Exception as exc:
            raise ex.excError(str(exc))
        if "error" in data:
            raise ex.excError(ret["error"])
        return ret

def do_action(action, array_name=None, node=None, **kwargs):
    o = Arrays()
    array = o.get_array(array_name)
    if array is None:
        raise ex.excError("array %s not found" % array_name)
    array.node = node
    node.log.handlers[1].setLevel(logging.CRITICAL)
    if not hasattr(array, action):
        raise ex.excError("not implemented")
    ret = getattr(array, action)(**kwargs)
    if ret is not None:
        print(json.dumps(ret, indent=4))

def main(argv, node=None):
    parser = OptParser(prog=PROG, options=OPT, actions=ACTIONS,
                       deprecated_actions=DEPRECATED_ACTIONS,
                       global_options=GLOBAL_OPTS)
    options, action = parser.parse_args(argv)
    kwargs = vars(options)
    do_action(action, node=node, **kwargs)

if __name__ == "__main__":
    try:
        ret = main(sys.argv)
    except ex.excError as exc:
        print(exc, file=sys.stderr)
        ret = 1
    except IOError as exc:
        if exc.errno == 32:
            # broken pipe
            ret = 1
        else:
            raise
    sys.exit(ret)


   0707010005c93d000081b40000000000000000000000015ab2175400003ab5000000660000000000000000000000000000002700000000root/usr/share/opensvc/lib/rcHp3par.py    from __future__ import print_function

import sys
import os
import json
import rcExceptions as ex
from subprocess import *
import time
from rcGlobalEnv import rcEnv
from rcUtilities import cache, clear_cache, justcall, which
import re
import datetime

if sys.version_info[0] < 3:
    from urllib2 import Request, urlopen
    from urllib2 import HTTPError
    from urllib import urlencode
else:
    from urllib.request import Request, urlopen, build_opener
    from urllib.error import HTTPError
    from urllib.parse import urlencode

try:
    import ConfigParser
except ImportError:
    import configparser as ConfigParser

if rcEnv.paths.pathbin not in os.environ['PATH']:
    os.environ['PATH'] += ":"+rcEnv.paths.pathbin

def reformat(s):
    lines = s.split('\n')
    for i, line in enumerate(lines):
        if '%' in line:
            # skip prompt
            x = line.index("%") + 2
            if x < len(line):
                line = line[x:]
            elif x == len(line):
                line = ""
        lines[i] = line
    s = '\n'.join(lines)
    s = s.replace("Pseudo-terminal will not be allocated because stdin is not a terminal.", "")
    return s.strip()

class Hp3pars(object):
    allowed_methods = ("ssh", "proxy", "cli")

    def __init__(self, objects=[], log=None):
        self.objects = objects
        if len(objects) > 0:
            self.filtering = True
        else:
            self.filtering = False
        self.arrays = []
        if not os.path.exists(rcEnv.paths.authconf):
            raise ex.excError("%s not found" % rcEnv.paths.authconf)
        conf = ConfigParser.RawConfigParser()
        conf.read(rcEnv.paths.authconf)
        m = {}

        for s in conf.sections():
            if not conf.has_option(s, "type") or \
               conf.get(s, "type") != "hp3par":
                continue

            if self.filtering and not s in self.objects:
                continue

            username = None
            manager = None
            key = None
            pwf = None

            try:
                method = conf.get(s, 'method')
            except:
                method = "ssh"

            if method not in self.allowed_methods:
                print("invalid method. allowed methods: %s" % ', '.join(self.allowed_methods))
                continue

            kwargs = {"log": log}
            try:
                manager = conf.get(s, 'manager')
                kwargs['manager'] = manager
            except Exception as e:
                kwargs['manager'] = s

            try:
                username = conf.get(s, 'username')
                key = conf.get(s, 'key')
                kwargs['username'] = username
                kwargs['key'] = key
            except Exception as e:
                if method in ("ssh"):
                    raise

            try:
                pwf = conf.get(s, 'pwf')
                kwargs['pwf'] = pwf
            except Exception as e:
                if method in ("cli"):
                    raise

            try:
                cli = conf.get(s, 'cli')
                kwargs['cli'] = cli
            except Exception as e:
                pass

            self.arrays.append(Hp3par(s, method, **kwargs))

        del(conf)

    def __iter__(self):
        for array in self.arrays:
            yield(array)

class Hp3par(object):
    def __init__(self, name, method, manager=None, username=None, key=None, pwf=None, cli="cli", svcname="", log=None):
        self.name = name
        self.manager = manager
        self.method = method
        self.username = username
        self.pwf = pwf
        self.cli = cli
        self.svcname = svcname
        self.key = key
        self.keys = ['showvv', 'showsys', 'shownode', "showcpg", "showport", "showversion"]
        self.uuid = None
        self.remotecopy = None
        self.virtualcopy = None
        self.log = log
        self.cache_sig_prefix = "hp3par."+self.manager+"."

    def ssh_cmd(self, cmd, log=False):
        _cmd = ['ssh', '-i', self.key, '@'.join((self.username, self.manager))]
        cmd = 'setclienv csvtable 1 ; setclienv nohdtot 1 ; ' + cmd + ' ; exit'
        return self._rcmd(_cmd, cmd, log=log)

    def proxy_cmd(self, cmd, log=False):
        url = 'https://%s/api/cmd/' % self.manager
        user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
        header = { 'User-Agent' : user_agent }

        values = {
          'array' : self.name,
          'cmd' : cmd,
          'svcname' : self.svcname,
          'uuid' : self.uuid,
        }

        data = urlencode(values)
        req = Request(url, data, header)

        try:
            f = build_opener().open(req)
            response = f.read()
            #response = urlopen(req)
        except Exception as e:
            return "", str(e)

        try:
            d = json.loads(response)
            ret = d['ret']
            out = d['out']
            err = d['err']
        except:
            ret = 1
            out = ""
            err = "unexpected proxy response format (not json)"

        if ret != 0:
            raise ex.excError("proxy error: %s" % err)

        return out, err

    def _rcmd(self, _cmd, cmd, log=False, retry=10):
        p = Popen(_cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE)
        p.stdin.write(cmd)
        out, err = p.communicate()
        out = reformat(out)
        err = reformat(err)

        if p.returncode != 0:
            if ("Connection closed by remote host" in err or "Too many local CLI connections." in err) and retry > 0:
                if log:
                    self.log.info("3par connection refused. try #%d" % retry)
                time.sleep(1)
                return self._rcmd(_cmd, cmd, log=log, retry=retry-1)
            if log:
                if len(out) > 0: self.log.info(out)
                if len(err) > 0: self.log.error(err)
            else:
                print(cmd)
                print(out)
            raise ex.excError("3par command execution error")

        return out, err

    def cli_cmd(self, cmd, log=False):
        if which(self.cli) is None:
            raise ex.excError("%s executable not found" % self.cli)

        os.environ["TPDPWFILE"] = self.pwf
        os.environ["TPDNOCERTPROMPT"] = "1"

        cmd = [self.cli, '-sys', self.name, '-nohdtot', '-csvtable'] + cmd.split()

        if log:
            s = " ".join(cmd)
            s = re.sub(r'password \w+', 'password xxxxx', s)
            self.log.info(s)

        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        out = reformat(out)
        err = reformat(err)

        if p.returncode != 0:
            if "The authenticity of the storage system cannot be established." in err:
                 raise ex.excError("3par connection error. array ssl cert is not trusted. open interactive session to trust it.")
            if ("Connection closed by remote host" in err or "Too many local CLI connections." in err) and retry > 0:
                if log:
                    self.log.info("3par connection refused. try #%d" % retry)
                time.sleep(1)
                return self._rcmd(_cmd, cmd, log=log, retry=retry-1)
            if log:
                if len(out) > 0: self.log.info(out)
                if len(err) > 0: self.log.error(err)
            else:
                print(' '.join(cmd))
                print(out)
            raise ex.excError("3par command execution error")

        return out, err

    def get_uuid(self):
        if self.uuid is not None:
            return self.uuid
        config = ConfigParser.RawConfigParser()
        config.read(rcEnv.paths.nodeconf)
        try:
            self.uuid = config.get("node", "uuid")
        except:
            pass
        return self.uuid

    def rcmd(self, cmd, log=False):
        if self.method == "ssh":
            return self.ssh_cmd(cmd, log=log)
        elif self.method == "cli":
            return self.cli_cmd(cmd, log=log)
        elif self.method == "proxy":
            self.get_uuid()
            return self.proxy_cmd(cmd, log=log)
        else:
            raise ex.excError("unsupported method %s set in auth.conf for array %s" % (self.method, self.name))

    def serialize(self, s, cols):
        return json.dumps(self.csv_to_list_of_dict(s, cols))

    def csv_to_list_of_dict(self, s, cols):
        l = []
        for line in s.splitlines():
            v = line.strip().split(',')
            h = {}
            for a, b in zip(cols, v):
                h[a] = b
            if len(h) > 1:
                l.append(h)
        return l

    @cache("has_virtualcopy")
    def has_virtualcopy(self):
        if self.virtualcopy is not None:
            return self.virtualcopy
        cmd = 'showlicense'
        s = self.rcmd(cmd)[0].strip("\n")
        self.virtualcopy = False
        for line in s.split('\n'):
            if "Virtual Copy" in line:
                self.virtualcopy = True
        return self.virtualcopy

    @cache("has_remotecopy")
    def has_remotecopy(self):
        if self.remotecopy is not None:
            return self.remotecopy
        cmd = 'showlicense'
        s = self.rcmd(cmd)[0].strip("\n")
        self.remotecopy = False
        for line in s.split('\n'):
            if "Remote Copy" in line:
                self.remotecopy = True
        return self.remotecopy

    def get_showvv(self):
        if self.has_remotecopy():
            cols = ["Name", "VV_WWN", "Prov", "CopyOf", "Tot_Rsvd_MB", "VSize_MB", "UsrCPG", "CreationTime", "RcopyGroup", "RcopyStatus"]
        else:
            cols = ["Name", "VV_WWN", "Prov", "CopyOf", "Tot_Rsvd_MB", "VSize_MB", "UsrCPG", "CreationTime"]
        cmd = 'showvv -showcols ' + ','.join(cols)
        print("%s: %s"%(self.name, cmd))
        s = self.rcmd(cmd)[0]
        return self.serialize(s, cols)

    def updatevv(self, vvnames=None, log=False):
        cmd = 'updatevv -f'
        if vvnames is None or len(vvnames) == 0:
            raise ex.excError("updatevv: no vv names specified")
        if vvnames:
            cmd += ' ' + ' '.join(vvnames)
        s = self.rcmd(cmd, log=log)[0]

    def showvv(self, vvnames=None, vvprov=None, cols=None):
        fdata = []
        data = self._showvv()
        for d in data:
            if vvnames and d["Name"] not in vvnames:
                continue
            if vvprov and d["Prov"] != vvprov:
                continue
            fdata.append(d)
        return fdata

    @cache("showvv")
    def _showvv(self):
        cols = ["Name", "CreationTime", "Prov"]
        cmd = 'showvv -showcols ' + ','.join(cols)
        out, err = self.rcmd(cmd)
        return self.csv_to_list_of_dict(out, cols)

    def showrcopy(self, rcg):
        """
        Remote Copy System Information
        Status: Started, Normal

        Group Information

        Name        ,Target    ,Status  ,Role      ,Mode    ,Options
        RCG.SVCTEST1,baie-pra,Started,Primary,Periodic,"Last-Sync 2014-03-05 10:19:42 CET , Period 5m, auto_recover,over_per_alert"
         ,LocalVV     ,ID  ,RemoteVV    ,ID  ,SyncStatus   ,LastSyncTime
         ,LXC.SVCTEST1.DATA01,2706,LXC.SVCTEST1.DATA01,2718,Synced,2014-03-05 10:19:42 CET
         ,LXC.SVCTEST1.DATA02,2707,LXC.SVCTEST1.DATA02,2719,Synced,2014-03-05 10:19:42 CET

        """
        out, err = self._showrcopy()

        if len(out) == 0:
            raise ex.excError("unable to fetch rcg status")

        lines = []
        cols_rcg = ["Name", "Target", "Status", "Role", "Mode"]
        cols_vv = ["LocalVV", "ID", "RemoteVV", "ID", "SyncStatus", "LastSyncTime"]

        # extract rcg block
        in_block = False
        for line in out.splitlines():
            if not in_block:
                if not line.startswith(rcg+","):
                    continue
                lines.append(line)
                in_block = True
            else:
                if not line.startswith(" "):
                    break
                lines.append(line)

        if len(lines) == 0:
            raise ex.excError("rcg does not exist")

        # RCG status
        rcg_s = lines[0]
        options_start = rcg_s.index('"')
        rcg_options = rcg_s[options_start+1:-1].split(",")
        rcg_options = map(lambda x: x.strip(), rcg_options)
        rcg_v = rcg_s[:options_start].split(",")
        rcg_data = {}
        for a, b in zip(cols_rcg, rcg_v):
            rcg_data[a] = b
        rcg_data["Options"] = rcg_options

        # VV status
        vv_l = []
        for line in lines[1:]:
            v = line.strip().strip(",").split(",")
            if len(v) != len(cols_vv):
                continue
            vv_data = {}
            for a, b in zip(cols_vv, v):
                vv_data[a] = b
            vv_data['LastSyncTime'] = self.s_to_datetime(vv_data['LastSyncTime'])
            vv_l.append(vv_data)
        data = {'rcg': rcg_data, 'vv': vv_l}
        return data

    def s_to_datetime(self, s):
        out, err, ret = justcall(["date", "--utc", "--date=%s" % s, '+%Y-%m-%d %H:%M:%S'])
        d = datetime.datetime.strptime(out.strip(), "%Y-%m-%d %H:%M:%S")
        return d

    @cache("showrcopy_groups")
    def _showrcopy(self):
        cmd = 'showrcopy groups'
        out, err = self.rcmd(cmd)
        return out, err

    def clear_showrcopy_cache(self):
        clear_cache("showrcopy_groups", o=self)

    def clear_caches(self):
        clear_cache("showvv", o=self)

    def get_showsys(self):
        cols = ["ID", "Name", "Model", "Serial", "Nodes", "Master", "TotalCap", "AllocCap", "FreeCap", "FailedCap"]
        cmd = 'showsys'
        print("%s: %s"%(self.name, cmd))
        s = self.rcmd(cmd)[0]
        return self.serialize(s, cols)

    def get_shownode(self):
        cols = ["Available_Cache", "Control_Mem", "Data_Mem", "InCluster", "LED", "Master", "Name", "Node", "State"]
        cmd = 'shownode -showcols ' + ','.join(cols)
        print("%s: %s"%(self.name, cmd))
        s = self.rcmd(cmd)[0]
        return self.serialize(s, cols)

    def get_showcpg(self):
        cols = ["Id", "Name", "Warn%", "VVs", "TPVVs", "Usr", "Snp", "Total", "Used", "Total", "Used", "Total", "Used"]
        cmd = 'showcpg'
        print("%s: %s"%(self.name, cmd))
        s = self.rcmd(cmd)[0]
        return self.serialize(s, cols)

    def get_showport(self):
        cols = ["N:S:P", "Mode", "State", "Node_WWN", "Port_WWN", "Type", "Protocol", "Label", "Partner", "FailoverState"]
        cmd = 'showport'
        print("%s: %s"%(self.name, cmd))
        s = self.rcmd(cmd)[0]
        return self.serialize(s, cols)

    def get_showversion(self):
        cmd = 'showversion -s'
        print("%s: %s"%(self.name, cmd))
        s = self.rcmd(cmd)[0].strip("\n")
        return json.dumps({"Version": s})

if __name__ == "__main__":
    o = Hp3pars()
    for hp3par in o:
        print(hp3par.get_showvv())
        print(hp3par.get_showsys())
        print(hp3par.get_shownode())
        print(hp3par.get_showcpg())
   0707010005c93e000081b40000000000000000000000015ab2175400000d82000000660000000000000000000000000000002600000000root/usr/share/opensvc/lib/rcIbmDs.py import sys
import os
import tempfile
from subprocess import *

try:
    import ConfigParser
except ImportError:
    import configparser as ConfigParser

import rcExceptions as ex
from rcGlobalEnv import rcEnv

if rcEnv.paths.pathbin not in os.environ['PATH']:
    os.environ['PATH'] += ":"+rcEnv.paths.pathbin

def dscli(cmd, hmc1, hmc2, username, pwfile, log=None):
    if len(hmc1) != 0:
       _hmc1 = ['-hmc1', hmc1]
    else:
       _hmc1 = []
    if len(hmc2) != 0:
       _hmc2 = ['-hmc2', hmc2]
    else:
       _hmc2 = []
    _cmd = ['/opt/ibm/dscli/dscli'] + _hmc1 + _hmc2 +['-user', username, '-pwfile', pwfile]
    if log is not None:
        log.info(cmd + ' | ' + ' '.join(_cmd))
    p = Popen(_cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
    out, err = p.communicate(input=cmd)
    out = out.replace("dscli>", "")
    err = err.replace("dscli>", "")
    if log is not None:
        if len(out) > 0:
            log.info(out)
        if len(err) > 0:
            log.error(err)
    if p.returncode != 0:
        #print >>sys.stderr, out, err
        raise ex.excError("dscli command execution error")
    return out, err

class IbmDss(object):
    def __init__(self, objects=[]):
        self.objects = objects
        if len(objects) > 0:
            self.filtering = True
        else:
            self.filtering = False
        self.arrays = []
        cf = rcEnv.paths.authconf
        if not os.path.exists(cf):
            return
        conf = ConfigParser.RawConfigParser()
        conf.read(cf)
        m = {}
        for s in conf.sections():
            if not conf.has_option(s, "type") or \
               conf.get(s, "type") != "ibmds":
                continue
            if self.filtering and not s in self.objects:
                continue
            pwfile = os.path.join(rcEnv.paths.pathvar, s+'.pwfile')
            if not os.path.exists(pwfile):
                raise ex.excError("%s does not exists. create it with 'dscli managepwfile ...'"%pwfile)

            try:
                username = conf.get(s, 'username')
                hmc1 = conf.get(s, 'hmc1')
                hmc2 = conf.get(s, 'hmc2')
                m[s] = [hmc1, hmc2, username, pwfile]
            except Exception as e:
                print("error parsing section", s, ":", e)
                pass
        del(conf)
        for name, creds in m.items():
            hmc1, hmc2, username, pwfile = creds
            self.arrays.append(IbmDs(name, hmc1, hmc2, username, pwfile))

    def __iter__(self):
        for array in self.arrays:
            yield(array)

    def get(self, array):
        for o in self.arrays:
            if o.name == array:
                return o
        raise ex.excError("%s not defined in auth.conf or not usable" % array)

class IbmDs(object):
    def __init__(self, name, hmc1, hmc2, username, pwfile):
        self.name = name
        self.username = username
        self.pwfile = pwfile
        self.hmc1 = hmc1
        self.hmc2 = hmc2
        self.keys = ['combo']

    def dscli(self, cmd, log=None):
        return dscli(cmd, self.hmc1, self.hmc2, self.username, self.pwfile, log=log)

    def get_combo(self):
        cmd = """setenv -banner off -header on -format delim
lsextpool
lsfbvol
lsioport
lssi
lsarray
lsarraysite
lsrank"""
        print("%s: %s"%(self.name, cmd))
        return self.dscli(cmd)[0]

if __name__ == "__main__":
    o = IbmDss()
    for ibmds in o:
        print(ibmds.get_combo())
  0707010005c93f000081b40000000000000000000000015ab2175400000bc6000000660000000000000000000000000000002700000000root/usr/share/opensvc/lib/rcIbmSvc.py    from rcUtilities import justcall, which
import rcExceptions as ex
import os

try:
    import ConfigParser
except ImportError:
    import configparser as ConfigParser

from rcGlobalEnv import rcEnv

if rcEnv.paths.pathbin not in os.environ['PATH']:
    os.environ['PATH'] += ":"+rcEnv.paths.pathbin

def rcmd(cmd, manager, username, key):
    _cmd = ['ssh', '-i', key, '@'.join((username, manager))]
    _cmd += [cmd]
    out, err, ret = justcall(_cmd)
    if ret != 0:
        print(_cmd)
        print(out)
        raise ex.excError("ssh command execution error")
    return out, err

class IbmSvcs(object):
    def __init__(self, objects=[]):
        self.objects = objects
        if len(objects) > 0:
            self.filtering = True
        else:
            self.filtering = False
        self.arrays = []
        cf = rcEnv.paths.authconf
        if not os.path.exists(cf):
            return
        conf = ConfigParser.RawConfigParser()
        conf.read(cf)
        m = {}
        for s in conf.sections():
            if not conf.has_option(s, "type") or \
               conf.get(s, "type") != "ibmsvc":
                continue
            if self.filtering and not s in self.objects:
                continue
            try:
                username = conf.get(s, 'username')
                key = conf.get(s, 'key')
                m[s] = [username, key]
            except:
                print("error parsing section", s)
                pass
        del(conf)
        for name, creds in m.items():
            username, key = creds
            self.arrays.append(IbmSvc(name, username, key))

    def __iter__(self):
        for array in self.arrays:
            yield(array)

class IbmSvc(object):
    def __init__(self, name, username, key):
        self.name = name
        self.username = username
        self.key = key
        #self.keys = ['lsvdisk']
        self.keys = ['lsvdisk', 'lsmdiskgrp', 'lsnode', 'lscluster', 'svc_product_id', 'lsfabric']

    def rcmd(self, cmd):
        return rcmd(cmd, self.name, self.username, self.key)

    def get_lsvdisk(self):
        cmd = 'lsvdisk -delim :'
        print("%s: %s"%(self.name, cmd))
        return self.rcmd(cmd)[0]

    def get_lsmdiskgrp(self):
        cmd = 'lsmdiskgrp -delim :'
        print("%s: %s"%(self.name, cmd))
        return self.rcmd(cmd)[0]

    def get_lsnode(self):
        cmd = 'svcinfo lsnode -delim !'
        print("%s: %s"%(self.name, cmd))
        return self.rcmd(cmd)[0]

    def get_lscluster(self):
        cmd = 'svcinfo lscluster -delim :'
        print("%s: %s"%(self.name, cmd))
        return self.rcmd(cmd)[0]

    def get_lsfabric(self):
        cmd = 'lsfabric -delim :'
        print("%s: %s"%(self.name, cmd))
        return self.rcmd(cmd)[0]

    def get_svc_product_id(self):
        cmd = 'echo $SVC_PRODUCT_ID'
        print("%s: %s"%(self.name, cmd))
        return self.rcmd(cmd)[0]

if __name__ == "__main__":
    o = IbmSvcs()
    for ibmsvc in o:
        print(ibmsvc.lsmdiskgrp())
  0707010005c940000081b40000000000000000000000015ab217540000121c000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/rcIfconfig.py  from subprocess import *
import logging
from rcGlobalEnv import *

class interface:
    def __str__(self):
        a = ['ifconfig %s:'%self.name]
        a += [' link_encap = ' + str(self.link_encap)]
        a += [' scope = ' + str(self.scope)]
        a += [' bcast = ' + str(self.bcast)]
        a += [' mtu = ' + str(self.mtu)]
        a += [' ipaddr = ' + str(self.ipaddr)]
        a += [' mask = ' + str(self.mask)]
        a += [' ip6addr = ' + str(self.ip6addr)]
        a += [' ip6mask = ' + str(self.ip6mask)]
        a += [' hwaddr = ' + self.hwaddr]
        a += [' flag_up = ' + str(self.flag_up)]
        a += [' flag_deprecated = ' + str(self.flag_deprecated)]
        a += [' flag_broadcast = ' + str(self.flag_broadcast)]
        a += [' flag_running = ' + str(self.flag_running)]
        a += [' flag_multicast = ' + str(self.flag_multicast)]
        a += [' flag_loopback = ' + str(self.flag_loopback)]
        if hasattr(self, 'groupname'):
            a += [' groupname = ' + str(self.groupname)]
        return '\n'.join(a)

    def __init__(self, name):
        self.name = name
        # defaults
        self.link_encap = ''
        self.scope = ''
        self.bcast = ''
        self.mask = ''
        self.mtu = ''
        self.ipaddr = ''
        self.ip6addr = []
        self.ip6mask = []
        self.hwaddr = ''
        self.flag_up = False
        self.flag_deprecated = False
        self.flag_broadcast = False
        self.flag_running = False
        self.flag_multicast = False
        self.flag_loopback = False

class ifconfig(object):
    def add_interface(self, name):
        i = interface(name)
        self.intf.append(i)

    def interface(self, name):
        for i in self.intf:
            if i.name == name:
                return i
        return None

    def has_interface(self, name):
        for i in self.intf:
            if i.name == name:
                return 1
        return 0

    def has_param(self, param, value):
        for i in self.intf:
            if not hasattr(i, param):
                continue

            if isinstance(getattr(i, param), list):
                if value in getattr(i, param):
                    return i
            else:
                if getattr(i, param) == value:
                    return i
        return None

    def get_matching_interfaces(self, param, value):
        l = []
        for i in self.intf:
            if not hasattr(i, param):
                continue
            if isinstance(getattr(i, param), list):
                if value in getattr(i, param):
                    l.append(i)
            else:
                if getattr(i, param) == value:
                    l.append(i)
        return l

    def __str__(self):
        s = ""
        for intf in self.intf:
            s += str(intf)
        s += "\nmcast: " + str(self.mcast_data)
        return s

    def __init__(self, mcast=False):
        self.intf = []
        self.mcast_data = {}

    def next_stacked_dev(self,dev):
        """Return the first available interfaceX:Y on  interfaceX
        """
        i = 1
        while True:
            stacked_dev = dev+':'+str(i)
            if not self.has_interface(stacked_dev):
                return stacked_dev
            i = i + 1

    def get_stacked_dev(self, dev, addr, log):
        """Upon start, a new interfaceX:Y will have to be assigned.
        Upon stop, the currently assigned interfaceX:Y will have to be
        found for ifconfig down
        """
        if ':' in addr:
            stacked_intf = self.has_param("ip6addr", addr)
        else:
            stacked_intf = self.has_param("ipaddr", addr)
        if stacked_intf is not None:
            if dev not in stacked_intf.name:
                base_intf = self.has_param("name", dev)
                if base_intf and hasattr(base_intf, "groupname"):
                    alt_intfs = [ i for i in self.get_matching_interfaces("groupname", base_intf.groupname) if i.name != base_intf.name and stacked_intf.name.startswith(i.name+":")]
                    if len(alt_intfs) == 1:
                        log.info("found %s plumbed on %s, in the same ipmp group than %s" % (addr, stacked_intf.name, dev))
                        return stacked_intf.name
                log.error("%s is plumbed but not on %s" % (addr, dev))
                return
            stacked_dev = stacked_intf.name
            log.debug("found matching stacked device %s" % stacked_dev)
        else:
            stacked_dev = self.next_stacked_dev(dev)
            log.debug("allocate new stacked device %s" % stacked_dev)
        return stacked_dev


0707010005c949000081b40000000000000000000000015ab2175400001d0e000000660000000000000000000000000000002700000000root/usr/share/opensvc/lib/rcLogger.py    import sys
import os
import gzip
import errno
import logging
import logging.handlers
from rcGlobalEnv import rcEnv
from subprocess import *

min_name_len = 10
namelen = 10
namefmt = "%-"+str(namelen)+"s"

try:
    type(PermissionError)
except:
    PermissionError = IOError

import platform
import re
from rcColor import colorize, color

DEFAULT_HANDLERS = ["file", "stream", "syslog"]

def namer(name):
    return name + ".gz"

def rotator(source, dest):
    with open(source, "rb") as sf:
        data = sf.read()
        with gzip.open(dest, "wb") as df:
            df.write(data)
    os.remove(source)

class ColorStreamHandler(logging.StreamHandler):
    def __init__(self, stream=None):
        logging.StreamHandler.__init__(self, stream)

    def format(self, record):
        text = logging.StreamHandler.format(self, record)
        def c(line):
            l = line.rstrip("\n").split(" - ")
            if len(l) < 3:
                return line

            l[0] = namefmt % l[0]
            l[0] = colorize(l[0], color.BOLD)
            l[1] = l[1].replace("ERROR", colorize("E", color.RED))
            l[1] = l[1].replace("WARNING", colorize("W", color.BROWN))
            l[1] = l[1].replace("DEBUG", colorize("D", color.LIGHTBLUE))
            l[1] = l[1].replace("INFO", " ")
            return " ".join(l)

        return c(text)

class LoggerHandler(logging.handlers.SysLogHandler):
    def __init__(self, facility=logging.handlers.SysLogHandler.LOG_USER):
        logging.Handler.__init__(self)
        self.facility = facility
        self.formatter = None

    def close(self):
        pass

    def emit(self, record):
        try:
            msg = self.format(record)
            cmd = ["logger", "-t", "", "-p", self.facility+"."+record.levelname.lower(), msg]
            p = Popen(cmd, stdout=None, stderr=None, stdin=None, close_fds=True)
            p.communicate()
        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            self.handleError(record)

def set_namelen(svcs=[], force=None):
    global namelen
    global namefmt

    maxlen = min_name_len
    for svc in svcs:
        if svc.disabled:
            continue
        svc_len = len(svc.svcname)

        # init with the "scheduler" length
        max_res_len = 10

        for r in svc.resources_by_id.values():
            if r is None:
                continue
            if r.is_disabled():
                continue
            l = len(r.rid)
            if r.subset:
                l += len(r.subset) + 2
            if l > max_res_len:
                max_res_len = l
        svc_len += max_res_len
        if svc_len > maxlen:
            maxlen = svc_len
    maxlen += len(rcEnv.nodename) + 1
    if force:
        maxlen = force
    namelen = maxlen
    namefmt = "%-"+str(namelen)+"s"

def initLogger(name, handlers=None):
    if handlers is None:
        handlers = DEFAULT_HANDLERS

    if name == rcEnv.nodename:
        logfile = os.path.join(rcEnv.paths.pathlog, "node") + '.log'
        debuglogfile = os.path.join(rcEnv.paths.pathlog, "node") + '.debug.log'
    else:
        if name.startswith(rcEnv.nodename):
            _name = name.replace(rcEnv.nodename+".", "", 1)
        else:
            _name = name
        logfile = os.path.join(rcEnv.paths.pathlog, _name) + '.log'
        debuglogfile = os.path.join(rcEnv.paths.pathlog, _name) + '.debug.log'
    log = logging.getLogger(name)
    log.propagate = False
    log.handlers = []

    if "file" in handlers:
        try:
            fileformatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
            filehandler = logging.handlers.RotatingFileHandler(logfile,
                                                               maxBytes=1*5242880,
                                                               backupCount=1)
            filehandler.setFormatter(fileformatter)
            filehandler.setLevel(logging.INFO)
            filehandler.rotator = rotator
            filehandler.namer = namer
            log.addHandler(filehandler)
        except PermissionError:
            pass

    if "stream" in handlers:
        streamformatter = logging.Formatter("%(name)s - %(levelname)s - %(message)s")
        streamhandler = ColorStreamHandler()
        streamhandler.setFormatter(streamformatter)
        log.addHandler(streamhandler)

        if '--debug' in sys.argv:
                rcEnv.loglevel = logging.DEBUG
                streamhandler.setLevel(logging.DEBUG)
        elif '--warn' in sys.argv:
                rcEnv.loglevel = logging.WARNING
                streamhandler.setLevel(logging.WARNING)
        elif '--error' in sys.argv:
                rcEnv.loglevel = logging.ERROR
                streamhandler.setLevel(logging.ERROR)
        else:
                rcEnv.loglevel = logging.INFO
                streamhandler.setLevel(logging.INFO)

    if "syslog" in handlers:
        try:
            import ConfigParser
        except ImportError:
            import configparser as ConfigParser
        config = ConfigParser.RawConfigParser({})
        try:
            config.read(rcEnv.paths.nodeconf)
        except:
            pass
        try:
            facility = config.get("syslog", "facility")
        except:
            facility = "daemon"
        try:
            lvl = config.get("syslog", "level").upper()
            lvl = getattr(logging, lvl)
        except:
            lvl = logging.INFO
        try:
            host = config.get("syslog", "host")
        except:
            host = None
        try:
            port = int(config.get("syslog", "port"))
        except:
            port = None
        address = None
        if host is None and port is None:
            if os.path.exists("/dev/log"):
                address = os.path.realpath("/dev/log")
            elif os.path.exists("/var/run/syslog"):
                address = os.path.realpath("/var/run/syslog")
        if address is None:
            if host is None:
                host = "localhost"
            if port is None:
                port = 514
            address = (host, port)

        syslogformatter = logging.Formatter("opensvc: %(name)s %(message)s")
        try:
            sysloghandler = logging.handlers.SysLogHandler(address=address, facility=facility)
        except Exception as e:
            if e.errno == errno.ENOTSOCK:
                # solaris /dev/log is a stream device
                sysloghandler = LoggerHandler(facility=facility)
            else:
                sysloghandler = None
        if sysloghandler:
            sysloghandler.setLevel(lvl)
            sysloghandler.setFormatter(syslogformatter)
            log.addHandler(sysloghandler)

    if "file" in handlers:
        try:
            fileformatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
            filehandler = logging.handlers.RotatingFileHandler(debuglogfile,
                                                               maxBytes=2*5242880,
                                                               backupCount=1)
            filehandler.setFormatter(fileformatter)
            filehandler.setLevel(logging.DEBUG)
            filehandler.rotator = rotator
            filehandler.namer = namer
            log.addHandler(filehandler)
        except PermissionError:
            pass

    log.setLevel(logging.DEBUG)
    return log


  0707010005c941000081b40000000000000000000000015ab2175400000ad1000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/rcIfconfigAIX.py   from subprocess import *
from rcUtilities import hexmask_to_dotted

import rcIfconfig

class ifconfig(rcIfconfig.ifconfig):
    def get_mac(self, intf):
        buff = self.get_netstat_in()
        for line in buff.split("\n"):
            l = line.split()
            if len(l) < 4:
                continue
            if l[0] != intf.name:
                continue
            if not l[2].startswith("link"):
                continue
            if '.' not in l[3]:
                return ""
            return l[3].replace('.', ':')
        return ""

    def get_netstat_in(self):
        if hasattr(self, "netstat_in_cache"):
            return self.netstat_in_cache
        cmd = ['netstat', '-in']
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            return ""
        self.netstat_in_cache = out
        return out

    def parse(self, out):
        prev = ''
        prevprev = ''
        for w in out.split():
            if 'flags=' in w:
                i = rcIfconfig.interface(prev.replace(':',''))
                i.hwaddr = self.get_mac(i)
                self.intf.append(i)

                # defaults
                i.link_encap = ''
                i.scope = ''
                i.bcast = ''
                i.mtu = ''
                i.ipaddr = []
                i.mask = []
                i.ip6addr = []
                i.ip6mask = []
                i.flag_up = False
                i.flag_broadcast = False
                i.flag_running = False
                i.flag_multicast = False
                i.flag_loopback = False

                flags = w.split('<')[1].split('>')[0].split(',')
                if 'UP' in flags:
                    i.flag_up = True
                if 'BROADCAST' in flags:
                    i.flag_broadcast = True
                if 'RUNNING' in flags:
                    i.flag_running = True
                if 'MULTICAST' in flags:
                    i.flag_multicast = True
                if 'LOOPBACK' in flags:
                    i.flag_loopback = True
            elif 'inet' == prev:
                i.ipaddr += [w]
            elif 'netmask' == prev:
                i.mask += [hexmask_to_dotted(w)]
            elif 'inet6' == prev:
                i.ip6addr += [w.split('/')[0]]
                i.ip6mask += [w.split('/')[1]]
            elif 'ether' == prev:
                i.hwaddr = w

            prevprev = prev
            prev = w

    def __init__(self, mcast=False):
        rcIfconfig.ifconfig.__init__(self)
        self.intf = []
        out = Popen(['ifconfig', '-a'], stdout=PIPE).communicate()[0]
        self.parse(out)

if __name__ == "__main__":
    ifaces = ifconfig(mcast=True)
    print(ifaces)

   0707010005c942000081b40000000000000000000000015ab2175400000b74000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/rcIfconfigDarwin.py    from subprocess import *
from rcUtilities import which

import rcIfconfig

class ifconfig(rcIfconfig.ifconfig):
    def parse(self, out):
        prev = ''
        prevprev = ''
        for w in out.split():
            if 'flags=' in w:
                i = rcIfconfig.interface(prev.replace(':',''))
                self.intf.append(i)

                # defaults
                i.link_encap = ''
                i.scope = ''
                i.bcast = ''
                i.mtu = ''
                i.ipaddr = []
                i.mask = []
                i.ip6addr = []
                i.ip6mask = []
                i.hwaddr = ''
                i.flag_up = False
                i.flag_broadcast = False
                i.flag_running = False
                i.flag_multicast = False
                i.flag_loopback = False

                flags = w.split('<')[1].split('>')[0].split(',')
                if 'UP' in flags:
                    i.flag_up = True
                if 'BROADCAST' in flags:
                    i.flag_broadcast = True
                if 'RUNNING' in flags:
                    i.flag_running = True
                if 'MULTICAST' in flags:
                    i.flag_multicast = True
                if 'LOOPBACK' in flags:
                    i.flag_loopback = True
            elif 'inet' == prev:
                i.ipaddr += [w]
            elif 'inet6' == prev:
                i.ip6addr += [w.split('%')[0]]
            elif 'netmask' == prev:
                i.mask += [w]
            elif 'prefixlen' == prev:
                i.ip6mask += [w]
            elif 'ether' == prev:
                i.hwaddr = w

            prevprev = prev
            prev = w

    def get_mcast(self):
        if which('netstat'):
            cmd = ['netstat', '-gn']
            out = Popen(cmd, stdout=PIPE).communicate()[0]
            return self.parse_mcast_netstat(out)

    def parse_mcast_netstat(self, out):
        lines = out.split('\n')
        found = False
        data = {}
        for i, line in enumerate(lines):
            if line.startswith('IPv4 Multicast'):
                found = True
                break
        if not found:
            return data
        if len(lines) == i+1:
            return data
        lines = lines[i+2:]
        for line in lines:
            if line.startswith('IPv6 Multicast') or line.startswith('Group'):
                continue
            try:
                addr, lladdr, intf = line.split()
            except:
                continue
            if intf not in data:
                data[intf] = [addr]
            else:
                data[intf] += [addr]
        return data

    def __init__(self, mcast=False):
        self.intf = []
        if mcast:
            self.mcast_data = self.get_mcast()
        else:
            self.mcast_data = {}
        out = Popen(['ifconfig', '-a'], stdout=PIPE).communicate()[0]
        self.parse(out)
0707010005c943000081b40000000000000000000000015ab21754000007a1000000660000000000000000000000000000003000000000root/usr/share/opensvc/lib/rcIfconfigFreeBSD.py   from __future__ import print_function

from subprocess import *

import rcIfconfig

class ifconfig(rcIfconfig.ifconfig):
    def parse(self, out):
        prev = ''
        prevprev = ''
        for w in out.split():
            if 'flags=' in w:
                i = rcIfconfig.interface(prev.replace(':',''))
                self.intf.append(i)

                # defaults
                i.link_encap = ''
                i.scope = ''
                i.bcast = ''
                i.mtu = ''
                i.ipaddr = []
                i.mask = []
                i.ip6addr = []
                i.ip6mask = []
                i.hwaddr = ''
                i.flag_up = False
                i.flag_broadcast = False
                i.flag_running = False
                i.flag_multicast = False
                i.flag_loopback = False

                flags = w.split('<')[1].split('>')[0].split(',')
                if 'UP' in flags:
                    i.flag_up = True
                if 'BROADCAST' in flags:
                    i.flag_broadcast = True
                if 'RUNNING' in flags:
                    i.flag_running = True
                if 'MULTICAST' in flags:
                    i.flag_multicast = True
                if 'LOOPBACK' in flags:
                    i.flag_loopback = True
            elif 'inet' == prev:
                i.ipaddr += [w]
            elif 'inet6' == prev:
                i.ip6addr += [w.split('%')[0]]
            elif 'netmask' == prev:
                i.mask += [w]
            elif 'prefixlen' == prev:
                i.ip6mask += [w]
            elif 'ether' == prev:
                i.hwaddr = w

            prevprev = prev
            prev = w

    def __init__(self, mcast=False):
        rcIfconfig.ifconfig.__init__(self, mcast=mcast)
        out = Popen(['ifconfig', '-a'], stdout=PIPE).communicate()[0]
        self.parse(out)

if __name__ == "__main__":
    o = ifconfig()
    print(o)
   0707010005c944000081b40000000000000000000000015ab217540000137b000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/rcIfconfigHP-UX.py from subprocess import *

import rcIfconfig
import rcExceptions as ex

class ifconfig(rcIfconfig.ifconfig):
    def parse(self, out):
        if len(out) == 0:
            return
        intf = out.split()[0]
        if intf[len(intf)-1] == ':':
            intf = intf[0:len(intf)-1]

        i = rcIfconfig.interface(intf)
        self.intf.append(i)

        # defaults
        i.link_encap = ''
        i.scope = ''
        i.bcast = ''
        i.mask = ''
        i.mtu = ''
        i.ipaddr = ''
        i.ip6addr = []
        i.ip6mask = []
        i.hwaddr = ''
        if i.name in self.hwaddr:
            i.hwaddr = self.hwaddr[i.name]
        i.flag_up = False
        i.flag_broadcast = False
        i.flag_running = False
        i.flag_multicast = False
        i.flag_loopback = False

        prev = ''
        for w in out.split():
            if 'broadcast' in prev:
                i.bcast = w
            elif 'netmask' in prev:
                if w == '0':
                    i.mask = "0.0.0.0"
                elif len(w) == 8:
                    i.mask = "%d.%d.%d.%d"%(
                        int(w[0:2], 16),
                        int(w[2:4], 16),
                        int(w[4:6], 16),
                        int(w[6:8], 16)
                    )
                else:
                    raise ex.excError("malformed ifconfig %s netmask: %s"%(intf, w))
            elif 'inet' == prev:
                i.ipaddr = w
            elif 'inet6' == prev:
                i.ip6addr += [w]
            elif 'prefix' == prev:
                i.ip6mask += [w]

            if 'UP' in w:
                i.flag_up = True
            if 'BROADCAST' in w:
                i.flag_broadcast = True
            if 'RUNNING' in w:
                i.flag_running = True
            if 'MULTICAST' in w:
                i.flag_multicast = True
            if 'LOOPBACK' in w:
                i.flag_loopback = True

            prev = w

    def get_mcast(self):
        cmd = ['netstat', '-gn']
        out = Popen(cmd, stdout=PIPE).communicate()[0]
        return self.parse_mcast(out)

    def parse_mcast(self, out):
        lines = out.split('\n')
        found = False
        data = {}
        for i, line in enumerate(lines):
            if line.startswith('Name'):
                found = True
                break
        if not found:
            return data
        if len(lines) == i+1:
            return data
        lines = lines[i+1:]
        for i, line in enumerate(lines):
            if len(line) == 0:
                break
            try:
                intf, addr, refcnt = line.split()
            except:
                continue
            if addr == "*":
                continue
            if intf not in data:
                data[intf] = [addr]
            else:
                data[intf] += [addr]
        if len(lines) <= i + 1:
            return data
        lines = lines[i+1:]
        for i, line in enumerate(lines):
            if line.startswith('Name'):
                found = True
                break
        if not found:
            return data
        if len(lines) == i+1:
            return data
        lines = lines[i+1:]
        for i, line in enumerate(lines):
            if len(line) == 0:
                break
            try:
                intf, addr, refcnt = line.split()
            except:
                continue
            if addr == "*":
                continue
            if intf not in data:
                data[intf] = [addr]
            else:
                data[intf] += [addr]
        return data

    def __init__(self, hwaddr=False, mcast=False):
        self.intf = []
        intf_list = []
        self.hwaddr = {}
        if mcast:
            self.mcast_data = self.get_mcast()
        else:
            self.mcast_data = {}
        if hwaddr:
            lines = Popen(['lanscan', '-i', '-a'], stdout=PIPE).communicate()[0].split('\n')
            for line in lines:
                l = line.split()
                if len(l) < 2:
                    continue
                mac = l[0].replace('0x','').lower()
                if len(mac) < 11:
                    continue
                mac_l = list(mac)
                for c in (10, 8, 6, 4, 2):
                    mac_l.insert(c, ':')
                self.hwaddr[l[1]] = ''.join(mac_l)
        out = Popen(['netstat', '-win'], stdout=PIPE).communicate()[0]
        for line in out.split('\n'):
            if len(line) == 0:
                continue
            if 'IPv4:' in line or 'IPv6' in line:
                continue
            intf = line.split()[0]
            intf_list.append(intf.replace('*', ''))
        for intf in intf_list:
            p = Popen(['ifconfig', intf], stdout=PIPE, stderr=PIPE)
            out = p.communicate()
            if "no such interface" in out[1]:
                continue
            elif p.returncode != 0:
                raise ex.excError
            self.parse(out[0])
 0707010005c945000081b40000000000000000000000015ab2175400002489000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/rcIfconfigLinux.py from subprocess import *
from rcUtilities import which, cidr_to_dotted
from rcGlobalEnv import rcEnv

import rcIfconfig
import copy

"""
ip addr:
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
...
4: eth0: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master bond0 qlen 1000
    link/ether 00:23:7d:a1:6f:96 brd ff:ff:ff:ff:ff:ff
6: sit0: <NOARP> mtu 1480 qdisc noop
    link/sit 0.0.0.0 brd 0.0.0.0
7: bond0: <BROADCAST,MULTICAST,MASTER,UP,LOWER_UP> mtu 1500 qdisc noqueue
    link/ether 00:23:7d:a1:6f:96 brd ff:ff:ff:ff:ff:ff
    inet 10.151.32.29/22 brd 10.151.35.255 scope global bond0
    inet 10.151.32.50/22 brd 10.151.35.255 scope global secondary bond0:1
    inet6 fe80::223:7dff:fea1:6f96/64 scope link
       valid_lft forever preferred_lft forever

"""

class ifconfig(rcIfconfig.ifconfig):
    def parse_ip(self, out):
        for line in out.splitlines():
            if len(line) == 0:
                continue
            if line[0] != " ":
                """
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
                """
                _line = line.split()
                ifname = _line[1].strip(":")
                if "@" in ifname:
                    ifkname = ifname[ifname.index("@"):]
                    ifname = ifname[:ifname.index("@")]
                else:
                    ifkname = None
                i = rcIfconfig.interface(ifname)
                i.ifkname = ifkname

                # defaults
                i.link_encap = ''
                i.scope = []
                i.bcast = []
                i.mask = []
                i.mtu = ''
                i.ipaddr = []
                i.ip6addr = []
                i.ip6mask = []
                i.hwaddr = ''
                i.flag_up = False
                i.flag_broadcast = False
                i.flag_running = False
                i.flag_multicast = False
                i.flag_loopback = False

                self.intf.append(i)

                prev = ''
                for w in _line:
                    if 'mtu' == prev:
                        i.mtu = w
                    elif w.startswith('<'):
                        w = w.strip('<').strip('>')
                        flags = w.split(',')
                        for w in flags:
                            if 'UP' == w:
                                i.flag_up = True
                            if 'BROADCAST' == w:
                                i.flag_broadcast = True
                            if 'RUNNING' == w:
                                i.flag_running = True
                            if 'MULTICAST' == w:
                                i.flag_multicast = True
                            if 'LOOPBACK' == w:
                                i.flag_loopback = True

                    prev = w
            elif line.strip().startswith("link"):
                """
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
                """
                _line = line.split()
                prev = ''
                for w in _line:
                    if 'link/' in w:
                        i.link_encap = w.split('/')[1]
                    elif 'link/ether' == prev:
                        i.hwaddr = w
                    prev = w
            elif line.strip().startswith("inet"):
                """
    inet 127.0.0.1/8 scope host lo
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
                """
                _line = line.split()
                if "global" in line and ":" in _line[-1]:
                    # clone parent intf and reset inet fields
                    ifname = line.split()[-1]
                    _i = copy.copy(i)
                    _i.name = ifname
                    _i.scope = []
                    _i.bcast = []
                    _i.mask = []
                    _i.ipaddr = []
                    _i.ip6addr = []
                    _i.ip6mask = []
                    self.intf.append(_i)
                else:
                    _i = i

                prev = ''
                for w in _line:
                    if 'inet' == prev :
                        try:
                            ipaddr, mask = w.split('/')
                        except:
                            # tun for example
                            continue
                        _i.ipaddr += [ipaddr]
                        _i.mask += [cidr_to_dotted(mask)]
                    elif 'inet6' == prev:
                        try:
                            ip6addr, ip6mask = w.split('/')
                        except:
                            # tun for example
                            continue
                        _i.ip6addr += [ip6addr]
                        _i.ip6mask += [ip6mask]
                    elif 'brd' == prev and 'inet' in line:
                        _i.bcast += [w]
                    elif 'scope' == prev and 'inet' in line:
                        _i.scope += [w]

                    prev = w


    def parse_ifconfig(self, out):
        prev = ''
        prevprev = ''
        for w in out.split():
            if w == 'Link':
                i = rcIfconfig.interface(prev)
                self.intf.append(i)

                # defaults
                i.link_encap = ''
                i.scope = ''
                i.bcast = ''
                i.mask = ''
                i.mtu = ''
                i.ipaddr = ''
                i.ip6addr = []
                i.ip6mask = []
                i.hwaddr = ''
                i.flag_up = False
                i.flag_broadcast = False
                i.flag_running = False
                i.flag_multicast = False
                i.flag_loopback = False
            elif 'encap:' in w:
                (null, i.link_encap) = w.split(':')
            elif 'Scope:' in w:
                (null, i.scope) = w.split(':')
            elif 'Bcast:' in w:
                (null, i.bcast) = w.split(':')
            elif 'Mask:' in w:
                (null, i.mask) = w.split(':')
            elif 'MTU:' in w:
                (null, i.mtu) = w.split(':')

            if 'inet' == prev and 'addr:' in w:
                (null, i.ipaddr) = w.split(':')
            if 'inet6' == prevprev and 'addr:' == prev:
                (ip6addr, ip6mask) = w.split('/')
                i.ip6addr += [ip6addr]
                i.ip6mask += [ip6mask]
            if 'HWaddr' == prev:
                i.hwaddr = w
            if 'UP' == w:
                i.flag_up = True
            if 'BROADCAST' == w:
                i.flag_broadcast = True
            if 'RUNNING' == w:
                i.flag_running = True
            if 'MULTICAST' == w:
                i.flag_multicast = True
            if 'LOOPBACK' == w:
                i.flag_loopback = True

            prevprev = prev
            prev = w

    def get_mcast(self):
        if which('netstat'):
            cmd = ['netstat', '-gn']
            out = Popen(cmd, stdout=PIPE).communicate()[0].decode()
            return self.parse_mcast_netstat(out)
        elif which(rcEnv.syspaths.ip):
            cmd = [rcEnv.syspaths.ip, 'maddr']
            out = Popen(cmd, stdout=PIPE).communicate()[0].decode()
            return self.parse_mcast_ip(out)

    def parse_mcast_netstat(self, out):
        lines = out.splitlines()
        found = False
        data = {}
        for i, line in enumerate(lines):
            if line.startswith('--'):
                found = True
                break
        if not found:
            return data
        if len(lines) == i+1:
            return data
        lines = lines[i+1:]
        for line in lines:
            try:
                intf, refcnt, addr = line.split()
            except:
                continue
            if intf not in data:
                data[intf] = [addr]
            else:
                data[intf] += [addr]
        return data

    def parse_mcast_ip(self, out):
        lines = out.splitlines()
        found = False
        data = {}
        for line in lines:
            if not line.startswith("	"):
                # new interface
                try:
                    name = line.split(":")[-1].strip()
                except Exception as e:
                    print(e)
                    break
                if name == "":
                    continue
                data[name] = []
                continue
            if "inet" not in line:
                continue
            data[name].append(line.split()[-1])
        return data

    def __init__(self, mcast=False, ip_out=None):
        self.intf = []
        if mcast:
            self.mcast_data = self.get_mcast()
        else:
            self.mcast_data = {}
        if ip_out:
            self.parse_ip(ip_out)
        elif which(rcEnv.syspaths.ip):
            out = Popen([rcEnv.syspaths.ip, 'addr'], stdout=PIPE).communicate()[0].decode()
            self.parse_ip(out)
        else:
            out = Popen(['ifconfig', '-a'], stdout=PIPE).communicate()[0].decode()
            self.parse_ifconfig(out)

if __name__ == "__main__":
    ifaces = ifconfig(mcast=True)
    print(ifaces)

   0707010005c946000081b40000000000000000000000015ab2175400000c42000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/rcIfconfigOSF1.py  from subprocess import *

import rcIfconfig
from rcUtilities import justcall

def ipv4_bitmask(s):
    if len(s) != 8:
        return
    import re
    regex = re.compile('^[0-9a-f]*$')
    if regex.match(s) is None:
        return
    r = []
    for i in range(4):
        pk = s[2*i:2*i+2]
        r.append(int(pk, 16))
    return '.'.join(map(str, r))

class ifconfig(rcIfconfig.ifconfig):

    def __init__(self, mcast=False):
        rcIfconfig.ifconfig.__init__(self, mcast=mcast)
        out = Popen(['ifconfig', '-a'], stdin=None, stdout=PIPE,stderr=PIPE,close_fds=True).communicate()[0]
        self.parse(out)

    def set_hwaddr(self, i):
        if i is None or i.hwaddr != '':
            return i
        if ":" in i.name:
            name = i.name.split(":")[0]
        else:
            name = i.name
        cmd = ["hwmgr", "get", "attribute", "-category", "network",
               "-a", "name="+name, "-a", "MAC_address"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return i
        for line in out.split('\n'):
            if not line.strip().startswith("MAC"):
                continue
            l = line.split("=")
            i.hwaddr = l[1].replace('-', ':').lower()
        return i

    def parse(self, out):
        i = None
        for l in out.split("\n"):
            if l == '' : continue
            if l[0]!=' ' :
                i = self.set_hwaddr(i)
                (ifname,ifstatus)=l.split(': ')

                i=rcIfconfig.interface(ifname)
                self.intf.append(i)

                # defaults
                i.link_encap = ''
                i.scope = ''
                i.bcast = []
                i.mtu = []
                i.mask = []
                i.ipaddr = []
                i.ip6addr = []
                i.ip6mask = []
                i.hwaddr = ''
                i.flag_up = False
                i.flag_broadcast = False
                i.flag_running = False
                i.flag_multicast = False
                i.flag_ipv4 = False
                i.flag_ipv6 = False
                i.flag_loopback = False

                if 'UP' in ifstatus : i.flag_up = True
                elif 'BROADCAST' in ifstatus : i.flag_broadcast = True
                elif 'RUNNING' in ifstatus   : i.flag_running = True
                elif 'MULTICAST' in ifstatus : i.flag_multicast = True
                elif 'IPv4' in ifstatus      : i.flag_ipv4 = True
                elif 'IPv6' in ifstatus      : i.flag_ipv6 = True
            else:
                n=0
                w=l.split()
                while n < len(w) :
                    [p,v]=w[n:n+2]
                    if p == 'inet' :
                        i.ipaddr.append(v)
                        i.mask.append(ipv4_bitmask(w[n+3]))
                    elif p == 'ipmtu' : i.mtu.append(v)
                    elif p == 'inet6' :
                        (a, m) = v.split('/')
                        i.ip6addr += [a]
                        i.ip6mask += [m]
                    n+=2
        i = self.set_hwaddr(i)


if __name__ == "__main__":
    for c in (ifconfig,) :
        help(c)
  0707010005c947000081b40000000000000000000000015ab21754000012e2000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/rcIfconfigSunOS.py from subprocess import *

import rcIfconfig

class ifconfig(rcIfconfig.ifconfig):

    def __init__(self, ifconfig=None, mcast=False):
        self.intf = []
        if mcast:
            self.mcast_data = self.get_mcast()
        else:
            self.mcast_data = {}
        if ifconfig is not None:
            out = ifconfig
        else:
            out = Popen(['/usr/sbin/ifconfig', '-a'], stdin=None, stdout=PIPE,stderr=PIPE,close_fds=True).communicate()[0]
        self.parse(out)

    def get_mcast(self):
        cmd = ['netstat', '-gn']
        out = Popen(cmd, stdout=PIPE).communicate()[0]
        return self.parse_mcast(out)

    def parse_mcast(self, out):
        lines = out.split('\n')
        found = False
        data = {}
        for i, line in enumerate(lines):
            if line.startswith('--'):
                found = True
                break
        if not found:
            return data
        if len(lines) == i+1:
            return data
        lines = lines[i+1:]
        for i, line in enumerate(lines):
            if len(line) == 0:
                break
            try:
                intf, addr, refcnt = line.split()
            except:
                continue
            if intf not in data:
                data[intf] = [addr]
            else:
                data[intf] += [addr]
        if len(lines) <= i + 1:
            return data
        lines = lines[i+1:]
        for i, line in enumerate(lines):
            if line.startswith('--'):
                found = True
                break
        if not found:
            return data
        if len(lines) == i+1:
            return data
        lines = lines[i+1:]
        for i, line in enumerate(lines):
            if len(line) == 0:
                break
            try:
                intf, addr, refcnt = line.split()
            except:
                continue
            if intf not in data:
                data[intf] = [addr]
            else:
                data[intf] += [addr]
        return data


    def set_hwaddr(self, i):
        if i is None or i.hwaddr != '' or ':' not in i.name:
            return i
        base_ifname, index = i.name.split(':')
        base_intf = self.interface(base_ifname)
        if base_intf is not None and len(base_intf.hwaddr) > 0:
            i.hwaddr = base_intf.hwaddr
        else:
            i.hwaddr = self.mac_from_arp(i.ipaddr)
        return i

    def mac_from_arp(self, ipaddr):
        cmd = ['/usr/sbin/arp', ipaddr]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            return ''
        for word in out.split():
            if ':' not in word:
                continue
            return word
        return ''

    def parse(self, out):
        i = None
        for l in out.split("\n"):
            if l == '' : break
            if l[0]!='\t' :
                i = self.set_hwaddr(i)
                (ifname,ifstatus)=l.split(': ')

                i=rcIfconfig.interface(ifname)
                self.intf.append(i)

                # defaults
                i.link_encap = ''
                i.scope = ''
                i.bcast = ''
                i.mask = ''
                i.mtu = ''
                i.ipaddr = ''
                i.ip6addr = []
                i.ip6mask = []
                i.hwaddr = ''
                i.groupname = ''
                i.flag_up = False
                i.flag_broadcast = False
                i.flag_running = False
                i.flag_multicast = False
                i.flag_ipv4 = False
                i.flag_ipv6 = False
                i.flag_loopback = False

                if 'UP' in ifstatus : i.flag_up = True
                if 'DEPRECATED' in ifstatus : i.flag_deprecated = True
                if 'BROADCAST' in ifstatus : i.flag_broadcast = True
                if 'RUNNING' in ifstatus   : i.flag_running = True
                if 'MULTICAST' in ifstatus : i.flag_multicast = True
                if 'IPv4' in ifstatus      : i.flag_ipv4 = True
                if 'IPv6' in ifstatus      : i.flag_ipv6 = True
            else:
                n=0
                w=l.split()
                while n < len(w) :
                    [p,v]=w[n:n+2]
                    if p == 'inet' : i.ipaddr=v
                    elif p == 'netmask' : i.mask=v
                    elif p == 'broadcast' : i.bcast=v
                    elif p == 'ether' : i.hwaddr=v
                    elif p == 'groupname' : i.groupname=v
                    elif p == 'inet6' :
                        (a, m) = v.split('/')
                        i.ip6addr += [a]
                        i.ip6mask += [m]
                    n+=2
        i = self.set_hwaddr(i)


if __name__ == "__main__":
    ifaces = ifconfig(mcast=True)
    print(ifaces)
  0707010005c948000081b40000000000000000000000015ab2175400000536000000660000000000000000000000000000003000000000root/usr/share/opensvc/lib/rcIfconfigWindows.py   from subprocess import *

import rcIfconfig
import wmi

class ifconfig(rcIfconfig.ifconfig):
    def parse(self, intf, intf_cf):
        if intf_cf.IPAddress is None:
            return
        i = rcIfconfig.interface(intf.NetConnectionID)
        self.intf.append(i)

        # defaults
        i.link_encap = ''
        i.scope = ''
        i.bcast = ''
        i.mask = []
        i.mtu = intf_cf.MTU
        i.ipaddr = []
        i.ip6addr = []
        i.ip6mask = []
        i.hwaddr = intf_cf.MACAddress
        try:
            i.flag_up = intf.NetEnabled
        except:
            i.flag_up = False
        i.flag_broadcast = False
        i.flag_running = False
        i.flag_multicast = False
        i.flag_loopback = False

        for idx, ip in enumerate(intf_cf.IPAddress):
            if ":" in ip:
                i.ip6addr.append(ip)
                i.ip6mask.append(intf_cf.IPsubnet[idx])
            else:
                i.ipaddr.append(ip)
                i.mask.append(intf_cf.IPsubnet[idx])

    def __init__(self, mcast=False):
        self.wmi = wmi.WMI()
        self.intf = []
        self.mcast_data = {}
        for n, nc in zip(self.wmi.Win32_NetworkAdapter(), self.wmi.Win32_NetworkAdapterConfiguration()):
            self.parse(n, nc)

if __name__ == "__main__" :
    o = ifconfig()
    print(o)
  0707010005c94a000081b40000000000000000000000015ab2175400000356000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/rcLoopDarwin.py    import os
import re
import plistlib

from rcGlobalEnv import *
from rcUtilities import call, which
import rcStatus
import rcExceptions as ex

def file_to_loop(f):
    """Given a file path, returns the disk device associated. For example,
    /path/to/file => /dev/disk0s1
    """
    if which('hdiutil') is None:
        return []
    if not os.path.isfile(f):
        return []
    (ret, out, err) = call(['hdiutil', 'info', '-plist'])
    if ret != 0:
        return []

    devs= []
    pl = plistlib.readPlistFromString(out)
    for image in pl['images']:
        if image.get('image-path') == f:
            for se in image['system-entities']:
                diskdevice = se.get('dev-entry')
                if diskdevice is not None:
                    devs.append(diskdevice)
                else:
                    return []

    return devs
  0707010005c94b000081b40000000000000000000000015ab217540000060d000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/rcLoopLinux.py import os
import re

from rcGlobalEnv import *
from rcUtilities import justcall, which
import rcStatus
import rcExceptions as ex

def file_to_loop(f):
    """
    Given a file path, returns the loop device associated. For example,
    /path/to/file => /dev/loop0
    """
    if which(rcEnv.syspaths.losetup) is None:
        return []
    if not os.path.isfile(f):
        return []
    if rcEnv.sysname != 'Linux':
        return []
    out, err, ret = justcall([rcEnv.syspaths.losetup, '-j', f])
    if len(out) == 0:
        return []

    # It's possible multiple loopdev are associated with the same file
    devs = []
    for line in out.split('\n'):
        l = line.split(':')
        if len(l) == 0:
            continue
        if len(l[0]) == 0:
            continue
        if not os.path.exists(l[0]):
            continue
        devs.append(l[0])
    return devs

def loop_to_file(f):
    """
    Given a loop dev, returns the loop file associated. For example,
    /dev/loop0 => /path/to/file
    """
    if which(rcEnv.syspaths.losetup) is None:
        return []
    if not os.path.exists(f):
        return []
    if rcEnv.sysname != 'Linux':
        return []
    out, err, ret = justcall([rcEnv.syspaths.losetup, f])
    if len(out) == 0:
        return []

    for line in out.split('\n'):
        l = line.split('(')
        if len(l) == 0:
            continue
        fpath = l[-1].rstrip(")")
        if len(fpath) == 0:
            continue
        if not os.path.exists(fpath):
            continue
        return fpath

   0707010005c94c000081b40000000000000000000000015ab2175400001f22000000660000000000000000000000000000002700000000root/usr/share/opensvc/lib/rcLvmAIX.py    from subprocess import *

"""
lsvg format
===========

VOLUME GROUP:       rootvg                   VG IDENTIFIER:  00082a6a0000d400000001321aa20bf2
VG STATE:           active                   PP SIZE:        64 megabyte(s)
VG PERMISSION:      read/write               TOTAL PPs:      959 (61376 megabytes)
MAX LVs:            256                      FREE PPs:       717 (45888 megabytes)
LVs:                11                       USED PPs:       242 (15488 megabytes)
OPEN LVs:           10                       QUORUM:         2 (Enabled)
TOTAL PVs:          1                        VG DESCRIPTORS: 2
STALE PVs:          0                        STALE PPs:      0
ACTIVE PVs:         1                        AUTO ON:        yes
MAX PPs per VG:     32512
MAX PPs per PV:     1016                     MAX PVs:        32
LTG size (Dynamic): 256 kilobyte(s)          AUTO SYNC:      no
HOT SPARE:          no                       BB POLICY:      relocatable

lsvg -l vgname format
=====================

rootvg:
LV NAME             TYPE       LPs     PPs     PVs  LV STATE      MOUNT POINT
hd5                 boot       1       1       1    closed/syncd  N/A
hd6                 paging     32      32      1    open/syncd    N/A
hd8                 jfs2log    1       1       1    open/syncd    N/A
hd4                 jfs2       16      16      1    open/syncd    /
hd2                 jfs2       40      40      1    open/syncd    /usr
hd9var              jfs2       16      16      1    open/syncd    /var
hd3                 jfs2       16      16      1    open/syncd    /tmp
hd1                 jfs2       16      16      1    open/syncd    /home
hd10opt             jfs2       16      16      1    open/syncd    /opt
lv_logs             jfs2       32      32      1    open/syncd    /logs
lv_moteurs          jfs2       56      56      1    open/syncd    /moteurs

lspv format
===========

hdisk0          00082a6a1aa20b3c                    rootvg          active
hdisk1          00082a6ae73c7bb6                    datavg          active

lspv -l pvname format
=====================

hdisk0:
LV NAME               LPs     PPs     DISTRIBUTION          MOUNT POINT
hd10opt               16      16      00..00..16..00..00    /opt
hd2                   40      40      00..00..40..00..00    /usr
hd9var                16      16      00..00..16..00..00    /var
hd3                   16      16      00..00..16..00..00    /tmp
hd1                   16      16      00..00..16..00..00    /home
hd5                   1       1       01..00..00..00..00    N/A
hd6                   32      32      00..00..32..00..00    N/A
hd8                   1       1       00..00..01..00..00    N/A
hd4                   16      16      00..00..16..00..00    /
lv_logs               32      32      00..32..00..00..00    /logs
lv_moteurs            56      56      00..56..00..00..00    /moteurs

"""

class InitVgError(Exception):
    pass

class Container(dict):
    def __call__(self,key):
        return self.__getitem__(key)

    def __getitem__(self, key):
        dict.__getitem__(self, key)

    def __setitem__(self, key, value):
        dict.__setitem__(self, key, value)

    def __getattr__(self, key):
        return self[key]

    def __setattr__(self, key, value):
        self[key] = value

    def __iadd__(self, o):
        dict.__setitem__(self, o.name, o)
        return self

class Vg(object):
    props = [{"prop": "ppsize", "key": "PP SIZE:", "consume": 2}]

    def __init__(self, name):
        self.name = name
        self.lv = Container()
        self.pv = Container()
        self.load_vg(name)
        self.load_lv(name)

    def __str__(self):
        l = []
        l.append("type: vg")
        l.append("name: %s"%self.name)
        l.append("pp size: %d MB"%self.ppsize)
        s = '\n'.join(l)

        for lv in self.lv.values():
            s += str(lv) + '\n'

        return s

    def parse_ppsize(self, l):
        self.ppsize = int(l[0])
        if 'megabyte' in l[1]:
            pass
        elif 'kilobyte' in l[1]:
            self.ppsize /= 1024
        elif 'gigabyte' in l[1]:
            self.ppsize *= 1024

    def load_vg(self, name):
        cmd = ['lsvg', name]
        p = Popen(cmd, stdout=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            raise InitVgError()
        for line in out.split('\n'):
            for p in self.props:
                if p['key'] not in line:
                    continue
                _line = line[line.index(p['key'])+len(p['key']):]
                l = _line.split()
                getattr(self, 'parse_'+p['prop'])(l[0:p['consume']])

    def load_lv(self, name):
        cmd = ['lsvg', '-l', name]
        p = Popen(cmd, stdout=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            raise InitVgError()
        for line in out.split('\n'):
            if 'LV NAME' in line:
                continue
            l = line.split()
            if len(l) < 6:
                continue
            _name, _type, _lps, _pps, _pvs, _state = l[0:6]
            _mntpt = ' '.join(l[6:])
            lv = Lv(_name)
            lv.type = _type
            lv.lps = int(_lps)
            lv.pps = int(_pps)
            lv.pvs = int(_pvs)
            lv.state = _state
            lv.mntpt = _mntpt
            lv.size = lv.pps * self.ppsize
            self.lv += lv

class Pv(object):
    def __init__(self, name):
        self.name = name
        self.lv_pps = {}
        self.load_lv_pps(name)

    def load_lv_pps(self, name):
        cmd = ['lspv', '-l', name]
        p = Popen(cmd, stdout=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            raise InitVgError()
        for line in out.split('\n'):
            if 'LV NAME' in line:
                continue
            l = line.split()
            if len(l) < 4:
                continue
            _name, _lps, _pps, _distrib = l[0:4]
            _mntpt = ' '.join(l[4:])
            if name in self.lv_pps:
                self.lv_pps[_name] += int(_pps)
            else:
                self.lv_pps[_name] = int(_pps)

class Lv(object):
    def __init__(self, name):
        self.name = name
        self.pv_size = {}

    def __str__(self):
        l = []
        l.append("type: lv")
        l.append("name: %s"%self.name)
        l.append("lv size: %d MB"%self.size)
        l.append("pv usage: %s"%self.pv_size.items())
        return '\n'.join(l)

class Lvm(object):
    def __init__(self):
        self.vg = Container()
        self.pv = Container()
        self.load_vg()
        self.load_pv()

    def __str__(self):
        s = ""
        for vgname, vg in self.vg.items():
            s += str(vg) + '\n\n'
        return s

    def load_vg(self):
        cmd = ['lsvg']
        p = Popen(cmd, stdout=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            raise InitVgError()
        for vg in out.split():
            self.vg += Vg(vg)

    def load_pv(self):
        cmd = ['lspv']
        p = Popen(cmd, stdout=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            raise InitVgError()
        for line in out.split('\n'):
            l = line.split()
            if len(l) != 4:
                continue
            _name, _id, _vgname, _state = l
            pv = Pv(_name)
            pv.id = _id
            pv.vgname = _vgname
            pv.state = _state
            self.pv += pv
            for lvname, lvpps in pv.lv_pps.items():
                vg, lv = self.find_lv(lvname)
                if lv is None:
                    continue
                lv.pv_size[pv.name] = vg.ppsize * lvpps

    def find_lv(self, lvname):
        for vg in self.vg.values():
            for lv in vg.lv.values():
                if lv.name == lvname:
                    return vg, lv
        return None, None

if __name__ == "__main__" :
    lvm = Lvm()
    print(lvm)
  0707010005c94d000081b40000000000000000000000015ab2175400002497000000660000000000000000000000000000002400000000root/usr/share/opensvc/lib/rcMd5.py   """
/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
 */

/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
rights reserved.

License to copy and use this software is granted provided that it
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
Algorithm" in all material mentioning or referencing this software
or this function.

License is also granted to make and use derivative works provided
that such works are identified as "derived from the RSA Data
Security, Inc. MD5 Message-Digest Algorithm" in all material
mentioning or referencing the derived work.

RSA Data Security, Inc. makes no representations concerning either
the merchantability of this software or the suitability of this
software for any particular purpose. It is provided "as is"
without express or implied warranty of any kind.

These notices must be retained in any copies of any part of this
documentation and/or software.
 */
"""

#/* Constants for MD5Transform routine.

S11 = 7
S12 = 12
S13 = 17
S14 = 22
S21 = 5
S22 = 9
S23 = 14
S24 = 20
S31 = 4
S32 = 11
S33 = 16
S34 = 23
S41 = 6
S42 = 10
S43 = 15
S44 = 21

PADDING = "\x80" + 63*"\0"   # do not overlook first byte again :-)

#/* F, G, H and I are basic MD5 functions.
def F(x, y, z): return (((x) & (y)) | ((~x) & (z)))

def G(x, y, z): return (((x) & (z)) | ((y) & (~z)))

def H(x, y, z): return ((x) ^ (y) ^ (z))

def I(x, y, z): return((y) ^ ((x) | (~z)))

#/* ROTATE_LEFT rotates x left n bits.

def ROTATE_LEFT(x, n):
    x = x & 0xffffffffL   # make shift unsigned
    return (((x) << (n)) | ((x) >> (32-(n)))) & 0xffffffffL

#/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
#Rotation is separate from addition to prevent recomputation.

def FF(a, b, c, d, x, s, ac):
    a = a + F ((b), (c), (d)) + (x) + (ac)
    a = ROTATE_LEFT ((a), (s))
    a = a + b
    return a # must assign this to a

def GG(a, b, c, d, x, s, ac):
    a = a + G ((b), (c), (d)) + (x) + (ac)
    a = ROTATE_LEFT ((a), (s))
    a = a + b
    return a # must assign this to a

def HH(a, b, c, d, x, s, ac):
    a = a + H ((b), (c), (d)) + (x) + (ac)
    a = ROTATE_LEFT ((a), (s))
    a = a + b
    return a # must assign this to a

def II(a, b, c, d, x, s, ac):
    a = a + I ((b), (c), (d)) + (x) + (ac)
    a = ROTATE_LEFT ((a), (s))
    a = a + b
    return a # must assign this to a


class md5:
    def __init__(self, initial=None):
        self.count = 0L
        self.state = (0x67452301L,
                      0xefcdab89L,
                      0x98badcfeL,
                      0x10325476L,)
        self.buffer = ""
        if initial:
            self.update(initial)

##/* MD5 block update operation. Continues an MD5 message-digest
##  operation, processing another message block, and updating the
##  context.
## */

##  /* Compute number of bytes mod 64 */
    def update(self, input):
        inputLen = len(input)
##  index = (unsigned int)((context->count[0] >> 3) & 0x3F);
        index = int(self.count >> 3) & 0x3F

##  /* Update number of bits */
        self.count = self.count + (inputLen << 3)

##  partLen = 64 - index;
        partLen = 64 - index

##  /* Transform as many times as possible.
        if inputLen >= partLen:
            self.buffer = self.buffer[:index] + input[:partLen]
            self.transform(self.buffer)
            i = partLen
            while i + 63 < inputLen:
                self.transform(input[i:i+64])
                i = i + 64
            index = 0
        else:
            i = 0

##  /* Buffer remaining input */
        self.buffer = self.buffer[:index] + input[i:inputLen]


##/* MD5 finalization. Ends an MD5 message-digest operation, writing the
##  the message digest and zeroizing the context.
## */

    def final(self):

##  /* Save number of bits */
        bits = Encode((self.count & 0xffffffffL, self.count>>32), 8)

##  /* Pad out to 56 mod 64.

        index = int((self.count >> 3) & 0x3f)
        if index < 56:
            padLen = (56 - index)
        else:
            padLen = (120 - index)
        self.update(PADDING[:padLen])

##  /* Append length (before padding) */
        self.update(bits)

##  /* Store state in digest */
        digest = Encode(self.state, 16)

##  /* Zeroize sensitive information.

        self.__dict__.clear()

        return digest

    digest = final  # alias

##/* MD5 basic transformation. Transforms state based on block.
## */

    def transform(self, block):
        a, b, c, d = state = self.state

        x = Decode(block, 64)

##  /* Round 1 */
        a = FF (a, b, c, d, x[ 0], S11, 0xd76aa478)#; /* 1 */
        d = FF (d, a, b, c, x[ 1], S12, 0xe8c7b756)#; /* 2 */
        c = FF (c, d, a, b, x[ 2], S13, 0x242070db)#; /* 3 */
        b = FF (b, c, d, a, x[ 3], S14, 0xc1bdceee)#; /* 4 */
        a = FF (a, b, c, d, x[ 4], S11, 0xf57c0faf)#; /* 5 */
        d = FF (d, a, b, c, x[ 5], S12, 0x4787c62a)#; /* 6 */
        c = FF (c, d, a, b, x[ 6], S13, 0xa8304613)#; /* 7 */
        b = FF (b, c, d, a, x[ 7], S14, 0xfd469501)#; /* 8 */
        a = FF (a, b, c, d, x[ 8], S11, 0x698098d8)#; /* 9 */
        d = FF (d, a, b, c, x[ 9], S12, 0x8b44f7af)#; /* 10 */
        c = FF (c, d, a, b, x[10], S13, 0xffff5bb1)#; /* 11 */
        b = FF (b, c, d, a, x[11], S14, 0x895cd7be)#; /* 12 */
        a = FF (a, b, c, d, x[12], S11, 0x6b901122)#; /* 13 */
        d = FF (d, a, b, c, x[13], S12, 0xfd987193)#; /* 14 */
        c = FF (c, d, a, b, x[14], S13, 0xa679438e)#; /* 15 */
        b = FF (b, c, d, a, x[15], S14, 0x49b40821)#; /* 16 */

## /* Round 2 */
        a = GG (a, b, c, d, x[ 1], S21, 0xf61e2562)#; /* 17 */
        d = GG (d, a, b, c, x[ 6], S22, 0xc040b340)#; /* 18 */
        c = GG (c, d, a, b, x[11], S23, 0x265e5a51)#; /* 19 */
        b = GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa)#; /* 20 */
        a = GG (a, b, c, d, x[ 5], S21, 0xd62f105d)#; /* 21 */
        d = GG (d, a, b, c, x[10], S22,  0x2441453)#; /* 22 */
        c = GG (c, d, a, b, x[15], S23, 0xd8a1e681)#; /* 23 */
        b = GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8)#; /* 24 */
        a = GG (a, b, c, d, x[ 9], S21, 0x21e1cde6)#; /* 25 */
        d = GG (d, a, b, c, x[14], S22, 0xc33707d6)#; /* 26 */
        c = GG (c, d, a, b, x[ 3], S23, 0xf4d50d87)#; /* 27 */
        b = GG (b, c, d, a, x[ 8], S24, 0x455a14ed)#; /* 28 */
        a = GG (a, b, c, d, x[13], S21, 0xa9e3e905)#; /* 29 */
        d = GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8)#; /* 30 */
        c = GG (c, d, a, b, x[ 7], S23, 0x676f02d9)#; /* 31 */
        b = GG (b, c, d, a, x[12], S24, 0x8d2a4c8a)#; /* 32 */

##  /* Round 3 */
        a = HH (a, b, c, d, x[ 5], S31, 0xfffa3942)#; /* 33 */
        d = HH (d, a, b, c, x[ 8], S32, 0x8771f681)#; /* 34 */
        c = HH (c, d, a, b, x[11], S33, 0x6d9d6122)#; /* 35 */
        b = HH (b, c, d, a, x[14], S34, 0xfde5380c)#; /* 36 */
        a = HH (a, b, c, d, x[ 1], S31, 0xa4beea44)#; /* 37 */
        d = HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9)#; /* 38 */
        c = HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60)#; /* 39 */
        b = HH (b, c, d, a, x[10], S34, 0xbebfbc70)#; /* 40 */
        a = HH (a, b, c, d, x[13], S31, 0x289b7ec6)#; /* 41 */
        d = HH (d, a, b, c, x[ 0], S32, 0xeaa127fa)#; /* 42 */
        c = HH (c, d, a, b, x[ 3], S33, 0xd4ef3085)#; /* 43 */
        b = HH (b, c, d, a, x[ 6], S34,  0x4881d05)#; /* 44 */
        a = HH (a, b, c, d, x[ 9], S31, 0xd9d4d039)#; /* 45 */
        d = HH (d, a, b, c, x[12], S32, 0xe6db99e5)#; /* 46 */
        c = HH (c, d, a, b, x[15], S33, 0x1fa27cf8)#; /* 47 */
        b = HH (b, c, d, a, x[ 2], S34, 0xc4ac5665)#; /* 48 */

##  /* Round 4 */
        a = II (a, b, c, d, x[ 0], S41, 0xf4292244)#; /* 49 */
        d = II (d, a, b, c, x[ 7], S42, 0x432aff97)#; /* 50 */
        c = II (c, d, a, b, x[14], S43, 0xab9423a7)#; /* 51 */
        b = II (b, c, d, a, x[ 5], S44, 0xfc93a039)#; /* 52 */
        a = II (a, b, c, d, x[12], S41, 0x655b59c3)#; /* 53 */
        d = II (d, a, b, c, x[ 3], S42, 0x8f0ccc92)#; /* 54 */
        c = II (c, d, a, b, x[10], S43, 0xffeff47d)#; /* 55 */
        b = II (b, c, d, a, x[ 1], S44, 0x85845dd1)#; /* 56 */
        a = II (a, b, c, d, x[ 8], S41, 0x6fa87e4f)#; /* 57 */
        d = II (d, a, b, c, x[15], S42, 0xfe2ce6e0)#; /* 58 */
        c = II (c, d, a, b, x[ 6], S43, 0xa3014314)#; /* 59 */
        b = II (b, c, d, a, x[13], S44, 0x4e0811a1)#; /* 60 */
        a = II (a, b, c, d, x[ 4], S41, 0xf7537e82)#; /* 61 */
        d = II (d, a, b, c, x[11], S42, 0xbd3af235)#; /* 62 */
        c = II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb)#; /* 63 */
        b = II (b, c, d, a, x[ 9], S44, 0xeb86d391)#; /* 64 */

        self.state = (0xffffffffL & (state[0] + a),
                      0xffffffffL & (state[1] + b),
                      0xffffffffL & (state[2] + c),
                      0xffffffffL & (state[3] + d),)

##  /* Zeroize sensitive information.

        del x

# end of the class. Now the helpers

import struct, string

def Encode(input, len):
    k = len >> 2
    res = apply(struct.pack, ("%iI" % k,) + tuple(input[:k]))
    return string.join(res, "")

def Decode(input, len):
    k = len >> 2
    res = struct.unpack("%iI" % k, input[:len])
    return list(res)

def test():
    print(`md5("/dev/sda").digest().encode('hex')`)
    #from md5 import new
    #print(`new("/dev/sda").digest()`)

if __name__=="__main__":
    test()
 0707010005c94e000081b40000000000000000000000015ab21754000009c9000000660000000000000000000000000000002700000000root/usr/share/opensvc/lib/rcMounts.py    import os
from subprocess import *
from rcUtilities import bdecode

class Mount:
    def __init__(self, dev, mnt, type, mnt_opt):
        self.dev = dev.rstrip('/')
        self.mnt = mnt.rstrip('/')
        if mnt is '/':
            self.mnt = mnt
        self.type = type
        self.mnt_opt = mnt_opt

    def __str__(self):
        return "Mount: dev[%s] mnt[%s] type[%s] options[%s]" % \
            (self.dev,self.mnt,self.type,self.mnt_opt)

class Mounts:
    src_dir_devs_cache = {}

    def __init__(self):
        """ OS dependent """
        self.mounts = []

    def __iter__(self):
        return iter(self.mounts)

    def match_mount(self):
        """ OS dependent """
        pass

    def mount(self, dev, mnt):
        for i in self.mounts:
            if self.match_mount(i, dev, mnt):
                return i
        return None

    def has_mount(self, dev, mnt):
        for i in self.mounts:
            if self.match_mount(i, dev, mnt):
                return True
        return False

    def has_param(self, param, value):
        for i in self.mounts:
            if getattr(i, param) == value:
                return i
        return None

    def sort(self, key='mnt', reverse=False):
        if len(self.mounts) == 0:
            return
        if key not in ('mnt', 'dev', 'type'):
            return
        self.mounts.sort(key=lambda x: getattr(x, key), reverse=reverse)

    def get_fpath_dev(self, fpath):
        last = False
        d = fpath
        while not last:
            d = os.path.dirname(d)
            if d in ("", None):
                return
            m = self.has_param("mnt", d)
            if m:
                return m.dev
            if d == os.sep:
                last = True

    def get_src_dir_dev(self, dev):
        """Given a directory path, return its hosting device
        """
        if dev in self.src_dir_devs_cache:
            return self.src_dir_devs_cache[dev]
        p = Popen(self.df_one_cmd + [dev], stdout=PIPE, stderr=STDOUT, close_fds=True)
        out, err = p.communicate()
        if p.returncode != 0:
            return
        out = bdecode(out).lstrip()
        lines = out.splitlines()
        if len(lines) == 2:
            out = lines[1]
        self.src_dir_devs_cache[dev] = out.split()[0]
        return self.src_dir_devs_cache[dev]

    def __str__(self):
        output="%s" % (self.__class__.__name__)
        for m in self.mounts:
            output+="\n  %s" % m.__str__()
        return output
   0707010005c94f000081b40000000000000000000000015ab2175400000d75000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/rcMountsAIX.py import rcMounts
#import resLoopAIX as Res
from rcUtilities import *

class Mounts(rcMounts.Mounts):
    df_one_cmd = ['df']

    def match_mount(self, i, dev, mnt):
        """Given a line of 'mount' output, returns True if (dev, mnt) matches
        this line. Returns False otherwize. Also care about weirdos like loops
        and binds, ...
        """
        if os.path.isdir(dev):
            is_bind = True
            src_dir_dev = self.get_src_dir_dev(dev)
        else:
            is_bind = False

        if i.mnt != mnt:
            return False
        if i.dev == dev:
            return True
#        if i.dev in Res.file_to_loop(dev):
#            return True
        if is_bind and i.dev == src_dir_dev:
            return True
        return False

    def __init__(self):
        self.mounts = []
        (ret, out, err) = call(['mount'])
        lines = out.split('\n')
        if len(lines) < 3:
            return
        for l in lines[2:]:
            if len(l) == 0:
                continue
            x = l.split()
            if x[0] == '-hosts':
                continue
            elif x[0][0] == '/':
                dev, mnt, type, null, null, null, mnt_opt = l.split()
            else:
                v = l.split()
                if len(v) == 7:
                    node, dev, mnt, type, null, null, null = l.split()
                    mntopt = ""
                if len(v) == 8:
                    node, dev, mnt, type, null, null, null, mnt_opt = l.split()
                else:
                    continue
            m = rcMounts.Mount(dev, mnt, type, mnt_opt)
            self.mounts.append(m)

"""
  node       mounted        mounted over    vfs       date        options
-------- ---------------  ---------------  ------ ------------ ---------------
         /dev/hd4         /                jfs2   Jun 14 19:42 rw,log=/dev/hd8
         /dev/hd2         /usr             jfs2   Jun 14 19:42 rw,log=/dev/hd8
         /dev/hd9var      /var             jfs2   Jun 14 19:42 rw,log=/dev/hd8
         /dev/hd3         /tmp             jfs2   Jun 14 19:42 rw,log=/dev/hd8
         /dev/hd1         /home            jfs2   Jun 14 19:48 rw,log=/dev/hd8
         /proc            /proc            procfs Jun 14 19:48 rw
         /dev/hd10opt     /opt             jfs2   Jun 14 19:48 rw,log=/dev/hd8

  node       mounted        mounted over    vfs       date        options
-------- ---------------  ---------------  ------ ------------ ---------------
         /dev/hd4         /                jfs2   Nov 29 11:22 rw,log=/dev/hd8
         /dev/hd2         /usr             jfs2   Nov 29 11:22 rw,log=/dev/hd8
         /dev/hd9var      /var             jfs2   Nov 29 11:22 rw,log=/dev/hd8
         /dev/hd3         /tmp             jfs2   Nov 29 11:23 rw,log=/dev/hd8
         /dev/hd1         /home            jfs2   Nov 29 11:23 rw,log=/dev/hd8
         /proc            /proc            procfs Nov 29 11:23 rw
         /dev/hd10opt     /opt             jfs2   Nov 29 11:23 rw,log=/dev/hd8
         /dev/lv_logs     /logs            jfs2   Nov 29 11:23 rw,log=/dev/hd8
         /dev/lv_moteurs  /moteurs         jfs2   Nov 29 11:23 rw,log=/dev/hd8
         -hosts           /net             autofs Nov 29 11:23 nosuid,vers=3,rw,nobrowse,ignore
x64lmwbief4 /install/outils  /mnt             nfs3   Dec 07 11:53

"""

if __name__ == "__main__" :
    help(Mounts)
    for m in Mounts():
        print(m)
   0707010005c950000081b40000000000000000000000015ab217540000053d000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/rcMountsDarwin.py  import rcMounts
from rcUtilities import *

class Mounts(rcMounts.Mounts):
    df_one_cmd = ['df', '-l']

    def match_mount(self, i, dev, mnt):
        """Given a line of 'mount' output, returns True if (dev, mnt) matches
        this line. Returns False otherwize. Also care about weirdos like loops
        and binds, ...
        """
        if os.path.isdir(dev):
            is_bind = True
            src_dir_dev = self.get_src_dir_dev(dev)
        else:
            is_bind = False

        if i.mnt != mnt:
            return False
        if i.dev == dev:
            return True
        if is_bind and i.dev == src_dir_dev:
            return True
        return False

    def __init__(self):
        self.mounts = []
        (ret, out, err) = call(['mount'])
        for l in out.split('\n'):
            words = l.split()
            if len(words) < 4:
                return
            dev = words[0]
            mnt = words[2]
            opts = ' '.join(words[3:]).strip('(').strip(')').split(', ')
            type = opts[0]
            if len(opts) < 3:
                mnt_opt = ''
            else:
                mnt_opt = ','.join(opts[2:])
            m = rcMounts.Mount(dev, mnt, type, mnt_opt)
            self.mounts.append(m)

if __name__ == "__main__" :
    help(Mounts)
    for m in Mounts():
        print(m)
   0707010005c951000081b40000000000000000000000015ab217540000053d000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/rcMountsFreeBSD.py import rcMounts
from rcUtilities import *

class Mounts(rcMounts.Mounts):
    df_one_cmd = ['df', '-l']

    def match_mount(self, i, dev, mnt):
        """Given a line of 'mount' output, returns True if (dev, mnt) matches
        this line. Returns False otherwize. Also care about weirdos like loops
        and binds, ...
        """
        if os.path.isdir(dev):
            is_bind = True
            src_dir_dev = self.get_src_dir_dev(dev)
        else:
            is_bind = False

        if i.mnt != mnt:
            return False
        if i.dev == dev:
            return True
        if is_bind and i.dev == src_dir_dev:
            return True
        return False

    def __init__(self):
        self.mounts = []
        (ret, out, err) = call(['mount'])
        for l in out.split('\n'):
            words = l.split()
            if len(words) < 4:
                return
            dev = words[0]
            mnt = words[2]
            opts = ' '.join(words[3:]).strip('(').strip(')').split(', ')
            type = opts[0]
            if len(opts) < 3:
                mnt_opt = ''
            else:
                mnt_opt = ','.join(opts[2:])
            m = rcMounts.Mount(dev, mnt, type, mnt_opt)
            self.mounts.append(m)

if __name__ == "__main__" :
    help(Mounts)
    for m in Mounts():
        print(m)
   0707010005c952000081b40000000000000000000000015ab2175400000485000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/rcMountsHP-UX.py   import rcMounts
from rcUtilities import *

class Mounts(rcMounts.Mounts):
    df_one_cmd = ['df', '-l']

    def match_mount(self, i, dev, mnt):
        """Given a line of 'mount' output, returns True if (dev, mnt) matches
        this line. Returns False otherwize. Also care about weirdos like loops
        and binds, ...
        """
        if os.path.isdir(dev):
            is_bind = True
            src_dir_dev = self.get_src_dir_dev(dev)
        else:
            is_bind = False

        if i.mnt != mnt:
            return False
        if i.dev == dev:
            return True
        if is_bind and i.dev == src_dir_dev:
            return True
        return False

    def __init__(self):
        self.mounts = []
        out, err, ret = justcall(['mount', '-v'])
        for l in out.split('\n'):
            if len(l.split()) != 12:
                return
            dev, null, mnt, null, type, mnt_opt, null, null, null, null, null, null = l.split()
            m = rcMounts.Mount(dev, mnt, type, mnt_opt.strip('()'))
            self.mounts.append(m)

if __name__ == "__main__" :
    help(Mounts)
    for m in Mounts():
        print(m)
   0707010005c953000081b40000000000000000000000015ab21754000004c5000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/rcMountsLinux.py   from rcGlobalEnv import rcEnv
import rcMounts
from rcLoopLinux import file_to_loop
from rcUtilities import *

class Mounts(rcMounts.Mounts):
    df_one_cmd = [rcEnv.syspaths.df, '-l']

    def match_mount(self, i, dev, mnt):
        """Given a line of 'mount' output, returns True if (dev, mnt) matches
        this line. Returns False otherwize. Also care about weirdos like loops
        and binds, ...
        """
        if i.mnt != mnt:
            return False
        if i.dev == dev:
            return True
        if i.dev in file_to_loop(dev):
            return True
        if os.path.isdir(dev):
            src_dir_dev = self.get_src_dir_dev(dev)
            if i.dev == src_dir_dev:
                return True
        return False

    def __init__(self):
        self.mounts = []
        (ret, out, err) = call([rcEnv.syspaths.mount])
        out = out.replace(" (deleted)", "")
        for l in out.split('\n'):
            if len(l.split()) != 6:
                return
            dev, null, mnt, null, type, mnt_opt = l.split()
            m = rcMounts.Mount(dev, mnt, type, mnt_opt.strip('()'))
            self.mounts.append(m)

if __name__ == "__main__" :
    for m in Mounts():
        print(m)
   0707010005c954000081b40000000000000000000000015ab21754000003a8000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/rcMountsOSF1.py    import rcMounts
from rcUtilities import justcall

class Mounts(rcMounts.Mounts):
    df_one_cmd = ['df', '-l']

    def match_mount(self, i, dev, mnt):
        """Given a line of 'mount' output, returns True if (dev, mnt) matches
        this line. Returns False otherwize. Also care about weirdos like loops
        and binds, ...
        """
        if i.mnt != mnt:
            return False
        if i.dev == dev:
            return True
        return False

    def __init__(self):
        self.mounts = []
        out, err, ret = justcall(['mount'])
        for l in out.split('\n'):
            l = l.replace(', ', ',')
            if len(l.split()) != 6:
                return
            dev, null, mnt, null, type, mnt_opt = l.split()
            m = rcMounts.Mount(dev, mnt, type, mnt_opt.strip('()'))
            self.mounts.append(m)

if __name__ == "__main__" :
    help(Mounts)
    for m in Mounts():
        print(m)
0707010005c955000081b40000000000000000000000015ab2175400000475000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/rcMountsSunOS.py   import rcMounts
from rcUtilities import *

class Mounts(rcMounts.Mounts):
    df_one_cmd = ["df", "-l"]

    def match_mount(self, i, dev, mnt):
        """Given a line of 'mount' output, returns True if (dev, mnt) matches
        this line. Returns False otherwize. Also care about weirdos like loops
        and binds, ...
        """
        if i.mnt != mnt:
            return False
        if i.dev == dev:
            return True
        return False

    def __init__(self):
        self.mounts = []
        (ret, out, err) = call(['mount','-p'], outdebug=False)
        for line in out.split('\n'):
            words=line.split()
            if len(words) < 6 :
                continue
            elif words[1]+words[4] != '--' :
                # ignore mount line with space in mountpoint or dev
                continue
            elif len(words) == 6 :
                words.append('-')
            dev, null, mnt, type, null, null, mnt_opt = words
            m = rcMounts.Mount(dev, mnt, type, mnt_opt.strip('()'))
            self.mounts.append(m)

if __name__ == "__main__" :
    help(Mounts)
    M=Mounts()
    print(M)

   0707010005c956000081b40000000000000000000000015ab21754000006c5000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/rcMountsWindows.py #
# Copyright (c) 2014 Christophe Varoqui <christophe.varoqui@opensvc.com>
# Copyright (c) 2014 Arnaud Veron <arnaud.veron@opensvc.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#

import rcMounts

class Mounts(rcMounts.Mounts):

    def match_mount(self, i, dev, mnt):
        """Given a line of 'mount' output, returns True if (dev, mnt) matches
        this line. Returns False otherwise.
        """
        if i.mnt != mnt:
            return False
        if i.dev == dev:
            return True
        return False

    def __init__(self, wmi=None):
        if wmi is None:
            import wmi
            wmi = wmi.WMI()
        self.mounts = []
        for volume in wmi.Win32_Volume():
            dev = volume.DeviceID
            mnt = volume.Name
            if mnt is None:
                mnt = ""
            type = volume.FileSystem
            mnt_opt = "NULL"    # quoi mettre d autre...
            m = rcMounts.Mount(dev, mnt, type, mnt_opt)
            self.mounts.append(m)

if __name__ == "__main__" :
    #help(Mounts)
    for m in Mounts():
        print(m)
   0707010005c957000081b40000000000000000000000015ab2175400002b98000000660000000000000000000000000000002700000000root/usr/share/opensvc/lib/rcNecIsm.py    from rcUtilities import which, justcall
import rcExceptions as ex
import os

class Nec(object):
    """
    BV OS Types
    ===========

    A2 the logical disk is operating on an ACOS-2 system.
    A4 the logical disk is operating on an ACOS-4 system.
    AX the logical disk is operating on an AIX system.
    CX the logical disk is operating on a Solaris system.
    LX the logical disk is operating on a Linux system.
    NX the logical disk is operating on an HP-UX system.
    SX the logical disk is operating on a SUPER-UX system.
    WN the logical disk is operating on a Windows system (excluding GPT disk).
    WG the logical disk is operating on a Windows system (GPT disk).

    """
    arrays = []
    view_bin = None
    sc_query_bin = None
    sc_linkinfo_bin = None
    sc_unlink_bin = None
    sc_link_bin = None
    sc_create_bin = None
    bv_os_types = ["-", "A2", "A4", "AX", "LX", "NX", "SX", "WN", "WG"]

    def get_bin(self, bin_attr, candidates):
        if getattr(self, bin_attr) is not None:
            return
        for bin in candidates:
            if which(bin) is not None:
                setattr(self, bin_attr, bin)
                break
        if getattr(self, bin_attr) is None:
            raise ex.excError('Can not find %s program in PATH' % ' or '.join(candidates))

    def get_view_bin(self):
        self.get_bin('view_bin', ['iSMcc_view', 'iSMview'])

    def get_sc_query_bin(self):
        self.get_bin('sc_query_bin', ['iSMsc_query'])

    def get_sc_linkinfo_bin(self):
        self.get_bin('sc_linkinfo_bin', ['iSMsc_linkinfo'])

    def get_sc_link_bin(self):
        self.get_bin('sc_link_bin', ['iSMsc_link'])

    def get_sc_create_bin(self):
        self.get_bin('sc_create_bin', ['iSMsc_create'])

    def get_sc_unlink_bin(self):
        self.get_bin('sc_unlink_bin', ['iSMsc_unlink'])

    def view_cmd(self, cmd, on_array=True):
        self.get_view_bin()
        cmd = [self.view_bin] + cmd
        if on_array:
            cmd += [self.name]
        return justcall(cmd)

    def sc_query_cmd(self, cmd):
        self.get_sc_query_bin()
        cmd = [self.sc_query_bin] + cmd
        return justcall(cmd)

    def sc_linkinfo_cmd(self, cmd):
        self.get_sc_linkinfo_bin()
        cmd = [self.sc_linkinfo_bin] + cmd
        return justcall(cmd)

    def sc_unlink_cmd(self, cmd):
        self.get_sc_unlink_bin()
        cmd = [self.sc_unlink_bin] + cmd
        self.log.info(' '.join(cmd))
        return justcall(cmd)

    def sc_link_cmd(self, cmd):
        self.get_sc_link_bin()
        cmd = [self.sc_link_bin] + cmd
        self.log.info(' '.join(cmd))
        return justcall(cmd)

    def sc_create_cmd(self, cmd):
        self.get_sc_create_bin()
        cmd = [self.sc_create_bin] + cmd
        self.log.info(' '.join(cmd))
        return justcall(cmd)

    def sc_create_ld(self, bv, sv):
        cmd = ['-bv', bv, '-sv', sv, '-bvflg', 'ld', '-svflg', 'ld']
        out, err, ret = self.sc_create_cmd(cmd)
        self.log.info(out)
        if ret != 0:
            raise ex.excError(err)

    def sc_unlink_ld(self, ld):
        cmd = ['-lv', ld, '-lvflg', 'ld']
        out, err, ret = self.sc_unlink_cmd(cmd)
        self.log.info(out)
        if ret != 0:
            raise ex.excError(err)

    def sc_link_ld(self, sv, ld):
        cmd = ['-lv', ld, '-sv', sv, '-lvflg', 'ld', '-svflg', 'ld']
        out, err, ret = self.sc_link_cmd(cmd)
        self.log.info(out)
        if ret != 0:
            raise ex.excError(err)

    def get_arrays(self):
        cmd = ['-d']
        out, err, ret = self.view_cmd(cmd, on_array=False)
        if ret != 0:
            self.refresh_vollist()
        out, err, ret = self.view_cmd(cmd, on_array=False)
        if ret != 0:
            raise ex.excError(err)

        """

--- Disk Array List ---
Product ID        Disk Array Name                   Resource State  Monitoring
D1-10             D1_10                             ready           running


--- Disk Array List ---
Product ID        Disk Array Name                   Resource State
Optima3600        Optima7_LMW                       ready


        """
        lines = out.split('\n')
        for line in lines:
            if len(line) == 0:
                continue
            if '---' in line:
                continue
            if 'Product ID' in line:
                continue
            l = line.split()
            if len(l) < 3:
                continue
            if self.filtering and l[1] not in self.objects:
                continue
            self.arrays.append(NecIsm(l[1]))

    def sc_linkinfo_ld(self, vol):
        """

Specified Volume Information
SV:LD Name      : test_src_0000_SV0014
   Type         : LX
   Special File : -
   State        : link   (test_src_0000_LV0064)
   Mode         : nr

Destination Volume Information
    LV:test_src_0000_LV0050     LX link   (test_src_0000_SV0016)    rw
    LV:test_src_0000_LV005A     LX link   (test_src_0000_SV0015)    rw
    LV:test_src_0000_LV0064     LX link   (test_src_0000_SV0014)    rw

"""
        cmd = ['-vol', vol, '-volflg', 'ld', '-lcl']
        out, err, ret = self.sc_linkinfo_cmd(cmd)
        if ret != 0:
            raise ex.excError(err)
        data = {'dst': []}
        for line in out.split('\n'):
            if line.startswith('SV:LD Name'):
                data['SV:LD Name'] = line.split(': ')[1]
            elif line.strip().startswith('Type'):
                data['Type'] = line.split(': ')[1]
            elif line.strip().startswith('Special File'):
                data['Special File'] = line.split(': ')[1]
            elif line.strip().startswith('State'):
                data['State'] = line.split(': ')[1]
            elif line.strip().startswith('Mode'):
                data['Mode'] = line.split(': ')[1]
            elif line.strip().startswith('LV:'):
                data['dst'].append(line.split(':')[1])
        return data

    def sc_query_ld(self, sv):
        """
BV Information
    LD Name      : test_src_0000
    Type         : LX
    Special File : /dev/sdc
    State        : normal
    Reserve Area : -

SV Information
  LX:test_src_0000_SV0014    ( -1) snap/active   [2014/03/24 11:16:16] link
        """
        cmd = ['-sv', sv, '-svflg', 'ld']
        out, err, ret = self.sc_query_cmd(cmd)
        if ret != 0:
            raise ex.excError(err)
        data = {'sv': []}
        for line in out.splitlines():
            line = line.strip()
            if line.startswith('LD Name'):
                data['LD Name'] = line.split(': ')[1]
            elif line.startswith('Type'):
                data['Type'] = line.split(': ')[1]
            elif line.startswith('Special File'):
                data['Special File'] = line.split(': ')[1]
            elif line.startswith('State'):
                data['State'] = line.split(': ')[1]
            elif line.startswith('Reserve Area'):
                data['Reserve Area'] = line.split(': ')[1]
            elif line.split(":", 1)[0] in bv_os_types:
                data['sv'].append(line[line.index(':')+1:])
        return data

    def sc_query_bv_detail(self, bv):
        """
        BV Information
            LD Name      : xxxxxxxxxxx_00CC
            Type         : LX
            Special File : /dev/sdaq
            State        : normal
            Reserve Area : -

        Pair Information
            SV:LD Name              : xxxxxxxxxxx_00cc_SV00ce
               Type                 : LX
               Generation(Attribute): -1(normal)
               Snap State           : snap/active        [2014/09/09 17:27:45]
               Create   Start Time  : 2014/09/09 17:27:45
               Processing Data Size : -
               Snapshot Data Size   : 47.4GB
               SV Guard             : off
               LV Link Status       : link
            LV:LD Name              : xxxxxxxxxxx_00cc_LV00cf
               Type                 : LX
               Special File         : /dev/sdar
               LV Access            : rw
            SV:LD Name              : xxxxxxxxxxx_00cc_SV00cd
               Type                 : LX
               Generation(Attribute): -2(normal)
               Snap State           : snap/active        [2014/09/09 17:19:12]
               Create   Start Time  : 2014/09/09 17:19:12
               Processing Data Size : -
               Snapshot Data Size   : 11.6GB
               SV Guard             : off
               LV Link Status       : link
            LV:LD Name              : xxxxxxxxxxx_00cc_LV00d0
               Type                 : LX
               LV Access            : rw
        """
        cmd = ['-bv', bv, '-bvflg', 'ld', '-detail']
        out, err, ret = self.sc_query_cmd(cmd)
        if ret != 0:
            raise ex.excError(err)
        data = {
          'sv': {},
          'lv': {}
        }
        section = ""
        for line in out.split('\n'):
            line = line.strip()
            if line.startswith("BV Information"):
                section = "bvinfo"
                continue
            elif line.startswith("Pair Information"):
                section = "pairinfo"
                continue

            if section == "bvinfo" and line.startswith("LD Name"):
                data['LD Name'] = line.split(': ')[1]
            elif section == "bvinfo" and line.startswith("State"):
                data['State'] = line.split(': ')[1]

            elif section.startswith("pairinfo") and line.startswith("SV:LD Name"):
                ld_name = line.split(': ')[1]
                if ld_name not in data['sv']:
                    data['sv'][ld_name] = {}
                section = "pairinfo_sv"
            elif section == "pairinfo_sv" and line.startswith("Snap State"):
                data['sv'][ld_name]["Snap State"] = line.split(': ')[1]


            elif section.startswith("pairinfo") and line.startswith("LV:LD Name"):
                ld_name = line.split(': ')[1]
                if ld_name not in data['lv']:
                    data['lv'][ld_name] = {}
                section = "pairinfo_lv"
            elif section == "pairinfo_lv" and line.startswith("Type"):
                data['lv'][ld_name]["Type"] = line.split(': ')[1]

        return data



class NecIsms(Nec):
    def __init__(self, objects=[]):
        self.objects = objects
        if len(objects) > 0:
            self.filtering = True
        else:
            self.filtering = False
        self.get_arrays()

    def __iter__(self):
        for array in self.arrays:
            yield(array)

    def refresh_vollist(self):
        if which('iSMvollist') is None:
            return
        cmd = ['iSMvollist', '-r']
        out, err, ret = justcall(cmd)

class NecIsm(Nec):
    def __init__(self, name):
        self.keys = ['all']
        self.name = name

    def get_all(self):
        cmd = ['-all']
        out, err, ret = self.view_cmd(cmd)
        return out

if __name__ == "__main__":
    o = NecIsms()
    for necism in o:
        print(necism.get_all())
        #print(o.sc_linkinfo_ld("test_src_0000_SV0014"))
        #print(o.sc_query_ld("test_src_0000_SV0014"))

0707010005c958000081b40000000000000000000000015ab2175400000b35000000660000000000000000000000000000002700000000root/usr/share/opensvc/lib/rcNetapp.py    from __future__ import print_function

import os
from subprocess import *

try:
    import ConfigParser
except ImportError:
    import configparser as ConfigParser

import rcExceptions as ex
from rcGlobalEnv import rcEnv
from rcUtilities import justcall

if rcEnv.paths.pathbin not in os.environ['PATH']:
    os.environ['PATH'] += ":"+rcEnv.paths.pathbin

class Netapps(object):
    def __init__(self, objects=[]):
        self.objects = objects
        if len(objects) > 0:
            self.filtering = True
        else:
            self.filtering = False
        self.arrays = []
        cf = rcEnv.paths.authconf
        if not os.path.exists(cf):
            return
        conf = ConfigParser.RawConfigParser()
        conf.read(cf)
        m = {}

        for s in conf.sections():
            if not conf.has_option(s, "type") or \
               conf.get(s, "type") != "netapp":
                continue

            if self.filtering and not s in self.objects:
                continue

            server = None
            username = None
            password = None

            kwargs = {}

            for key in ("server", "username", "key"):
                try:
                    kwargs[key] = conf.get(s, key)
                except:
                    print("missing parameter: %s", s)
                    continue

            self.arrays.append(Netapp(s, **kwargs))

        del(conf)

    def __iter__(self):
        for array in self.arrays:
            yield(array)

class Netapp(object):
    def __init__(self, name, server=None, username=None, key=None):
        self.name = name
        self.server = server
        self.username = username
        self.key = key
        self.keys = [
          'aggr_show_space',
          'lun_show_v',
          'lun_show_m',
          'sysconfig_a',
          'df',
          'df_S',
          'fcp_show_adapter',
        ]

    def rcmd(self, cmd):
        cmd = ["ssh", "-o", "StrictHostKeyChecking=no", "-i", self.key, self.username+"@"+self.server, cmd]
        out, err, ret = justcall(cmd)
        return out, err

    def get_aggr_show_space(self):
        out, err = self.rcmd("aggr show_space -m")
        return out

    def get_lun_show_v(self):
        out, err = self.rcmd("lun show -v")
        return out

    def get_lun_show_m(self):
        out, err = self.rcmd("lun show -m")
        return out

    def get_sysconfig_a(self):
        out, err = self.rcmd("sysconfig -a")
        return out

    def get_df(self):
        out, err = self.rcmd("df")
        return out

    def get_df_S(self):
        out, err = self.rcmd("df -S")
        return out

    def get_fcp_show_adapter(self):
        out, err = self.rcmd("fcp show adapter")
        return out

if __name__ == "__main__":
    o = Netapps()
    for netapp in o:
        print(netapp.get_aggr_show_space())
        break

   0707010005c959000081b40000000000000000000000015ab2175400002b64000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/rcNexenta.py   import sys
import json
import base64
import os

if sys.version_info[0] < 3:
    from urllib2 import Request, urlopen
    from urllib2 import HTTPError, URLError
else:
    from urllib.request import Request, urlopen
    from urllib.error import HTTPError, URLError

import rcExceptions as ex
from rcGlobalEnv import rcEnv

class logger(object):
    def __init__(self):
        pass

    def info(self, msg):
        print(msg)

    def warning(self, msg):
        print(msg)

    def error(self, msg):
        print(msg)

class Nexenta(object):
    def __init__(self, head, log=None):
        self.object_type_cache = {}
        self.head = head
        self.auto_prefix = "svc:/system/filesystem/zfs/auto-sync:"
        self.username = None
        self.password = None
        self.port = 2000
        if log is not None:
            self.log = log
        else:
            self.log = logger()

    def init(self):
        if self.username is not None and self.password is not None:
            return
        import ConfigParser
        cf = rcEnv.paths.authconf
        self.conf = ConfigParser.RawConfigParser()
        self.conf.read(cf)
        if not self.conf.has_section(self.head):
            raise ex.excError("no auth information for head %s"%self.head)
        if not self.conf.has_option(self.head, "username"):
            raise ex.excError("no username information for head %s"%self.head)
        if not self.conf.has_option(self.head, "password"):
            raise ex.excError("no password information for head %s"%self.head)
        self.username = self.conf.get(self.head, "username")
        self.password = self.conf.get(self.head, "password")
        if self.conf.has_option(self.head, "port"):
            self.port = self.conf.get(self.head, "port")
        self.url = 'https://%(head)s:%(port)d/rest/nms/ <https://%(head)s:%(port)d/rest/nms/>'%dict(head=self.head, port=self.port)

    def rest(self, obj, method, params):
        self.init()
        data = {"method": method, "params": params, "object": obj}
        data = json.dumps(data)
        request = Request(self.url, data)
        base64string = base64.encodestring('%s:%s' % (self.username, self.password))[:-1]
        request.add_header('Authorization', 'Basic %s' % base64string)
        request.add_header('Content-Type' , 'application/json')
        try:
            response = urlopen(request)
        except URLError:
            raise ex.excError("unreachable head %s"%self.head)
        response = json.loads(response.read())
        return response

    def dbus_auth_keys_list(self):
        data = self.rest("appliance", "dbus_auth_keys_list", [])
        if data['error'] is not None:
            raise ex.excError(data['error'])
        return data['result']

    def ssh_list_bindings(self):
        data = self.rest("appliance", "ssh_list_bindings", [])
        if data['error'] is not None:
            raise ex.excError(data['error'])
        return data['result']

    def ssh_unbind(self, user, hostport, force="0"):
        data = self.rest("appliance", "ssh_unbind", [user, hostport, force])
        if data['error'] is not None:
            raise ex.excError(data['error'])
        return data['result']

    def ssh_bind(self, user, hostport, password):
        data = self.rest("appliance", "ssh_bind", [user, hostport, password])
        if data['error'] is not None:
            raise ex.excError(data['error'])
        return data['result']

    def autosync_get_names(self):
        data = self.rest("autosync", "get_names", [''])
        if data['error'] is not None:
            raise ex.excError(data['error'])
        return data['result']

    def autosync_disable(self, name):
        if not name.startswith(self.auto_prefix):
            name = self.auto_prefix+name
        data = self.rest("autosync", "disable", [name])
        if data['error'] is not None:
            raise ex.excError(data['error'])
        return data['result']

    def autosync_enable(self, name):
        if not name.startswith(self.auto_prefix):
            name = self.auto_prefix+name
        data = self.rest("autosync", "enable", [name])
        if data['error'] is not None:
            raise ex.excError(data['error'])
        return data['result']

    def autosync_execute(self, name):
        if not name.startswith(self.auto_prefix):
            name = self.auto_prefix+name
        data = self.rest("autosync", "execute", [name])
        if data['error'] is not None:
            raise ex.excError(data['error'])
        return data['result']

    def autosync_get_state(self, name):
        if not name.startswith(self.auto_prefix):
            name = self.auto_prefix+name
        data = self.rest("autosync", "get_state", [name])
        if data['error'] is not None:
            raise ex.excError(data['error'])
        return data['result']

    def autosync_set_prop(self, name, prop, value):
        if not name.startswith(self.auto_prefix):
            name = self.auto_prefix+name
        data = self.rest("autosync", "set_child_prop", [name, prop, value])
        if data['error'] is not None:
            raise ex.excError(data["error"])
        return data['result']

    def autosync_get_props(self, name):
        if not name.startswith(self.auto_prefix):
            name = self.auto_prefix+name
        data = self.rest("autosync", "get_child_props", [name, ''])
        if data['error'] is not None:
            raise ex.excError(data["error"])
        return data['result']

    def autosync_register(self, name):
        if not name.startswith(self.auto_prefix):
            name = self.auto_prefix+name
        data = self.rest("runner", "register", [name, {}, {}])
        if data['error'] is not None:
            raise ex.excError(data["error"])
        return data['result']

    def zvol_clone(self, src, dst):
        data = self.rest("zvol", "clone", [src, dst])
        if data['error'] is not None:
            raise ex.excError(data["error"])

    def folder_clone(self, src, dst):
        data = self.rest("folder", "clone", [src, dst])
        if data['error'] is not None:
            raise ex.excError(data["error"])

    def clone(self, src, dst):
        snap = "@".join([src, dst.replace('/','_')])
        object_type = self.object_type(src)
        if object_type == "folder":
            self.folder_clone(snap, dst)
        elif object_type == "zvol":
            self.zvol_clone(snap, dst)
        else:
            raise ex.excError("object type %s is not cloneable"%str(object_type))

    def snapshot_create(self, src, dst, recursive=0):
        dst = dst.replace('/','_')
        object_type = self.object_type(src)
        if object_type == "folder":
            self.folder_snapshot(src, dst, recursive)
        elif object_type == "zvol":
            self.zvol_snapshot(src, dst, recursive)
        else:
            raise ex.excError("object type %s is not snapable"%str(object_type))

    def zvol_snapshot(self, src, dst, recursive=0):
        data = self.rest("zvol", "create_snapshot", [src, dst, recursive])
        if data['error'] is not None:
            raise ex.excError(data["error"])

    def folder_snapshot(self, src, dst, recursive=0):
        snap = "@".join([src, dst])
        data = self.rest("snapshot", "create", [snap, recursive])
        if data['error'] is not None:
            raise ex.excError(data["error"])

    def snapshot_destroy(self, src, dst, recursive=''):
        snap = "@".join([src, dst])
        data = self.rest("snapshot", "destroy", [snap, recursive])
        if data['error'] is not None:
            raise ex.excError(data["error"])

    def snapshot_get_names(self):
        data = self.rest("snapshot", "get_names", [''])
        if data['error'] is not None:
            raise ex.excError(data["error"])
        return data['result']

    def folder_get_names(self):
        data = self.rest("folder", "get_names", [''])
        if data['error'] is not None:
            raise ex.excError(data["error"])
        for folder in data['result']:
            self.object_type_cache[folder] = "folder"
        return data['result']

    def zvol_get_names(self):
        data = self.rest("zvol", "get_names", [''])
        if data['error'] is not None:
            raise ex.excError(data["error"])
        for zvol in data['result']:
            self.object_type_cache[folder] = "zvol"
        return data['result']

    def object_type(self, o):
        if o in self.object_type_cache:
            return self.object_type_cache[o]
        if o in self.folder_get_names():
            self.object_type_cache[o] = "folder"
            return "folder"
        elif o in self.zvol_get_names():
            self.object_type_cache[o] = "zvol"
            return "zvol"
        else:
            raise ex.excError("can not determine type of object %s"%o)

    def set_prop(self, name, prop, val):
        otype = self.object_type(name)
        return self._set_prop(otype, name, prop, val)

    def _set_prop(self, otype, name, prop, val):
        data = self.rest(otype, "set_child_prop", [name, prop, val])
        if data['error'] is not None:
            raise ex.excError(data["error"])
        return data['result']

    def get_props(self, name):
        otype = self.object_type(name)
        return self._get_props(otype, name)

    def _get_props(self, otype, name):
        data = self.rest(otype, "get_child_props", [name, ''])
        if data['error'] is not None:
            raise ex.excError(data["error"])
        return data['result']

    def set_can_mount(self, name):
        p = self.get_props(name)
        if not 'canmount' in p:
            return
        self.set_prop(name, "canmount", "on")

    def autosync_set_can_mount(self, name):
        folders = self.folder_get_names()
        props = self.autosync_get_props(name)

        if props['zfs/from-host'] == 'localhost':
            synchead = props['zfs/from-fs']
        else:
            synchead = props['zfs/to-fs']

        synchead = synchead.lstrip('/')
        for folder in folders:
            if not folder.startswith(synchead):
                continue
            self.set_can_mount(folder)
            self.log.info("set 'canmount = on' on folder %s"%folder)

    def snapclone(self, src, dst):
        self.snapshot_create(src, dst)
        self.clone(src, dst)

if __name__ == "__main__":
    o = Nexenta("nexenta1")
    #names = o.autosync_register("test")
    #print(o.set_prop("vol1/folder1", "canmount", "on"))
    #print(o.get_props("vol1/folder1"))
    print(Nexenta("nexenta1").dbus_auth_keys_list())
    print(Nexenta("nexenta2").dbus_auth_keys_list())
    #print(o.autosync_set_can_mount("vol1-folder1-000"))
    #names = o.autosync_get_names()
    #print(o.autosync_set_prop(names[0], "zfs/reverse_capable", "1"))
    #print(o.autosync_get_state(names[0]))
    #print(o.autosync_get_props(names[0]))
    #print(o.snapshot_create("vol1/zvol1", "test"))
    #print(o.snapshot_get_names())
    #print(o.snapshot_destroy("vol1/zvol1", "test"))
    #print(o.snapclone("vol1/folder1", "vol1/folder2"))

0707010005c95a000081b40000000000000000000000015ab21754000003eb000000660000000000000000000000000000002500000000root/usr/share/opensvc/lib/rcNode.py  import os
import sys
from rcGlobalEnv import rcEnv

try:
    import ConfigParser
except ImportError:
    import configparser as ConfigParser

def node_get_node_env():
    import codecs
    config = ConfigParser.RawConfigParser()
    if not os.path.exists(rcEnv.paths.nodeconf):
        return 'TST'
    with codecs.open(rcEnv.paths.nodeconf, "r", "utf8") as f:
        if sys.version_info[0] >= 3:
            config.read_file(f)
        else:
            config.readfp(f)
    if config.has_section('node'):
        if config.has_option('node', 'env'):
            return config.get('node', 'env')
        if config.has_option('node', 'host_mode'):
            # deprecated
            return config.get('node', 'host_mode')
    elif config.has_option('DEFAULT', 'env'):
        return config.get('DEFAULT', 'env')
    return 'TST'

def discover_node():
    """Fill rcEnv class with information from node discovery
    """
    if not hasattr(rcEnv, "env"):
        rcEnv.node_env = node_get_node_env()

 0707010005c95b000081b40000000000000000000000015ab217540000049b000000660000000000000000000000000000002400000000root/usr/share/opensvc/lib/rcNsr.py   from rcUtilities import justcall, which
import rcExceptions as ex
import os
import socket

class Nsr(object):
    def __init__(self):
        if not which('mminfo'):
            raise ex.excError('mminfo not found')
        self.keys = ['mminfo']

    def get_mminfo(self):
        os.environ["LC_TIME"] = "en_DK"
        cmd = ['mminfo', '-x', 'c;', '-q', 'savetime>=last day', '-r', 'client,name,group,totalsize,savetime(30),ssretent(30),volume,level,ssid(53)']
        print(' '.join(cmd))
        lines = justcall(cmd)[0].split('\n')[1:]
        for li, line in enumerate(lines):
            if len(line) == 0:
                continue
            try:
                i = line.index(';')
            except ValueError:
                continue
            client = line[:i]
            try:
                a = socket.getaddrinfo(client, None)
            except socket.gaierror:
                a = []
            if len(a) > 0:
                ip = a[0][-1][0]
            else:
                ip = client
            lines[li] = ip + line[i:]
        return unicode('\n'.join(lines), errors='ignore')


if __name__ == "__main__":
    o = Nsr()
    print(o.get_mminfo())
 0707010005c95c000081b40000000000000000000000015ab2175400003654000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/rcOptParser.py """
Helper module to handle optparser configuration.

Define a reference of supported keywords, their supported options, and methods
to format contextualized help messages.
"""

from __future__ import print_function
from __future__ import unicode_literals

import os
import sys
import optparse
import textwrap
import rcColor
from rcUtilities import term_width, is_string
import rcExceptions as ex


class OsvcHelpFormatter(optparse.TitledHelpFormatter):
    def format_option(self, option):
        result = []
        opts = self.option_strings[option]
        opt_width = self.help_position - self.current_indent - 2
        if len(opts) > opt_width:
            opts = "%*s%s\n" % (self.current_indent, "", opts)
            indent_first = self.help_position
        else:                       # start help on same line as opts
            opts = "%*s%-*s  " % (self.current_indent, "", opt_width, opts)
            indent_first = 0
        result.append(opts)
        if option.help:
            help_text = self.expand_default(option)
            help_lines = []
            for block in help_text.splitlines():
                help_lines += textwrap.wrap(block, self.help_width)
            result.append("%*s%s\n" % (indent_first, "", help_lines[0]))
            result.extend(["%*s%s\n" % (self.help_position, "", line)
                           for line in help_lines[1:]])
        elif opts[-1] != "\n":
            result.append("\n")
        result.append("\n")
        return "".join(result).replace("``", "`")

class OptionParserNoHelpOptions(optparse.OptionParser):
    def format_help(self, formatter=None):
        if formatter is None:
            formatter = self.formatter
        result = []
        if self.usage:
            result.append(self.get_usage() + "\n")
        if self.description:
            result.append(self.format_description(formatter) + "\n")
        result.append(self.format_epilog(formatter))
        return "".join(result)

class OptParser(object):
    """
    A class wrapping the optparse module use, adding some features:
    * contextualized help depending on action prefix
    * colors
    * layout tweaks
    """

    def __init__(self, args=None, prog="", options=None, actions=None,
                 deprecated_actions=None, actions_translations=None,
                 global_options=None, svc_select_options=None, colorize=True,
                 width=None, formatter=None, indent=6):
        self.parser = None
        self.args = args
        self.prog = prog
        self.options = options
        self.actions = actions
        self.deprecated_actions = deprecated_actions if deprecated_actions else []
        self.actions_translations = actions_translations if actions_translations else {}
        self.global_options = global_options if global_options else []
        self.svc_select_options = svc_select_options if svc_select_options else []
        self.colorize = colorize
        self.width = term_width() if width is None else width

        self.usage = self.prog + " [ OPTIONS ] COMMAND\n\n"
        self.indent = indent
        self.subsequent_indent = " " * self.indent
        if formatter is None:
            self.formatter = OsvcHelpFormatter(self.indent,
                                               self.indent+2,
                                               self.width)
        else:
            self.formatter = formatter
        self.formatter.format_heading = lambda x: "\n"
        self.get_parser()

    def get_valid_actions(self, section, action):
        """
        Given a section and an action prefix, return the list of
        valid actions
        """
        valid_actions = []
        for candidate_action in sorted(self.actions[section]):
            if is_string(action) and \
               not candidate_action.startswith(action):
                continue
            if isinstance(action, list) and candidate_action not in action:
                continue
            valid_actions.append(candidate_action)
        return valid_actions

    def format_options(self, section, action):
        """
        Format the possible options for a spectific action.
        """
        desc = ""
        parser = OptionParserNoHelpOptions(formatter=self.formatter, add_help_option=False)
        for option in self.actions[section][action].get("options", []):
            parser.add_option(option)
        for option in self.global_options:
            if self.svclink() and option in self.svc_select_options:
                continue
            parser.add_option(option)
        desc += self.subsequent_indent + parser.format_option_help()
        return desc

    def format_action(self, section, action, options=True):
        """
        Format an candidate action for the help message.
        The action message may or may include the possible options,
        dependendin on the value of the options parameter.
        """
        fancya = self.prog + " " + action.replace('_', ' ')
        if self.colorize:
            desc = "  " + rcColor.colorize(fancya, rcColor.color.BOLD)
        else:
            desc = "  " + fancya
        desc += '\n\n'
        wrapper = textwrap.TextWrapper(width=self.width-self.indent, replace_whitespace=False)
        text = self.actions[section][action]["msg"]
        for line in wrapper.wrap(text):
            for _line in line.splitlines():
                desc += self.subsequent_indent+_line.replace("``", "`")+"\n"
        desc += '\n'

        if options:
            desc += self.format_options(section, action)

        desc += '\n'
        return desc

    def format_desc(self, svc=False, action=None, options=True):
        """
        Format and return a svcmgr parser help message, contextualized to display
        only actions matching the action argument.
        """
        desc = self.usage
        for section in sorted(self.actions):
            valid_actions = self.get_valid_actions(section, action)
            if len(valid_actions) == 0:
                continue

            desc += section + '\n'
            desc += '-' * len(section)
            desc += "\n\n"
            for valid_action in valid_actions:
                if svc and not hasattr(svc, valid_action):
                    continue
                try:
                    desc += self.format_action(section, valid_action, options=options)
                except ValueError:
                    # http://bugs.python.org/issue13107 triggered by lxc-attach
                    # term environment.
                    desc += action + "\n"
        return desc[0:-2]

    def supported_actions(self):
        """
        Return the list of actions supported by svcmgr.
        """
        actions = []
        for section in self.actions:
            actions += self.actions[section].keys()
        actions += self.deprecated_actions
        return actions

    @staticmethod
    def svclink():
        """
        Return True if the service link was used to call svcmgr,
        else return False
        """
        return os.environ.get("OSVC_SERVICE_LINK", False)

    def actions_next_words(self, base=""):
        """
        From actions ["do_this", "do_that", "or_do_that"]:
        base="do"    => set([this, that, do])
        base="or_do" => set([that])
        """
        data = set()
        if base == "":
            prefix = base
        else:
            prefix = base + "_"
        prefix_len = len(prefix)
        for section in self.actions:
            for action in self.actions[section]:
                if base != "" and not action.startswith(base+'_'):
                    continue
                words = action[prefix_len:].split("_")
                if len(words) == 0:
                    continue
                data.add(words[0])
        return data

    def actions_all(self):
        data = []
        for section in self.actions:
            data += list(self.actions[section].keys())
        return data

    def develop_action(self, args):
        """
        From "ed conf" return "edit_config"
        """
        developed_args = []
        for idx, arg in enumerate(args):
            data = self.actions_next_words("_".join(developed_args))
            if arg in data:
                developed_args.append(arg)
                continue
            matching = [word for word in data if word.startswith(arg)]
            if len(matching) == 1:
                developed_args.append(matching[0])
                continue
            elif len(matching) == 0 and idx > 0:
                break
            else:
                # ambiguous
                return "_".join(developed_args+args[idx:])
        developed_action = "_".join(developed_args)
        if developed_action in self.actions_all():
            return developed_action
        while True:
            data = self.actions_next_words("_".join(developed_args))
            if len(data) == 0:
                break
            if len(data) == 1:
                developed_args.append(list(data)[0])
                continue
            break
        return "_".join(developed_args)

    def get_action_from_args(self, args, options):
        """
        Check if the parsed command args list has at least one element to be
        interpreted as an action. Raise if not, else return the action name
        formatted as a '_' joined string.
        """
        if len(args) is 0:
            if options.parm_help:
                self.print_full_help()
            else:
                self.print_short_help()

        action = self.develop_action(args)

        if action in self.actions_translations:
            data = self.actions_translations[action]
            if isinstance(data, dict):
                action = data["action"]
                options = data["mangle"](options)
            else:
                action = data

        return action, options

    def get_parser(self):
        """
        Setup an optparse parser
        """
        if self.parser is not None:
            return

        try:
            from version import version
        except ImportError:
            try:
                from node import Node
                version = Node().agent_version()
            except IndexError:
                version = "dev"

        self.version = self.prog + " version " + version

        self.parser = OptionParserNoHelpOptions(
            version=self.version,
            add_help_option=False,
        )

        for option in self.options.values():
            if self.svclink() and option in self.svc_select_options:
                continue
            self.parser.add_option(option)

    def set_full_usage(self):
        """
        Setup for display of all actions and options.
        Used by the man page generator.
        """
        usage = self.format_desc(action=None, options=True)
        self.parser.set_usage(usage)

    def parse_args(self, argv=None):
        """
        Parse system's argv, validate options compatibility with the action
        and return options and action
        """
        if argv is not None:
            self.args = argv
        else:
            self.args = sys.argv[1:]

        # parse a first time with all possible options to never fail on
        # undefined option.
        options, args = self.parser.parse_args(self.args)
        action, options = self.get_action_from_args(args, options)

        # now we know the action. and we know if --help was set.
        # we can prepare a contextualized usage message.
        usage = self.format_desc(action=action, options=options.parm_help)
        self.parser.set_usage(usage)

        if options.parm_help or action not in self.supported_actions():
            self.print_context_help(action, options)

        # parse a second time with only options supported by the action
        # so we can raise on options incompatible with the action
        parser = OptionParserNoHelpOptions(
            version=self.version,
            usage=usage,
            add_help_option=False,
        )

        action_options = []
        for section_data in self.actions.values():
            if action not in section_data:
                continue
            action_options = section_data[action].get("options", [])
        for option in action_options + self.global_options:
            if self.svclink() and option in self.svc_select_options:
                continue
            parser.add_option(option)
        options_discarded, args_discarded = parser.parse_args(self.args, optparse.Values())
        return options, action


    def print_full_help(self):
        """
        Reset the parser usage to the full actions list and their options.
        Then trigger a parser error, which displays the help message.
        """
        usage = self.format_desc()
        self.parser.set_usage(usage)
        if self.args is None:
            self.parser.error("Missing action")

    def print_short_help(self):
        """
        Reset the parser usage to a short message presenting only the most
        currently used actions. Then trigger a parser error, which displays the
        help message.
        """
        highlight_actions = ["start", "stop", "print_status"]
        usage = self.format_desc(action=highlight_actions, options=False) + \
                "\n\nOptions:\n" + \
                "  -h, --help       Display more actions and options\n"
        self.parser.set_usage(usage)
        if self.args is None:
            self.parser.error("Missing action")

    def print_context_help(self, action, options):
        """
        Trigger a parser error, which displays the help message contextualized
        for the action prefix.
        """
        if options.parm_help:
            self.parser.print_help()
            raise ex.excError
        else:
            self.parser.error("Invalid action: %s" % str(action))
0707010005c95d000081b40000000000000000000000015ab2175400000080000000660000000000000000000000000000002300000000root/usr/share/opensvc/lib/rcOs.py    class Os(object):
    def reboot(self):
        print("not implemented")

    def crash(self):
        print("not implemented")
0707010005c95e000081b40000000000000000000000015ab217540000007a000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/rcOsHP-UX.py   import rcOs
from rcUtilities import justcall

class Os(rcOs.Os):
    def reboot(self):
        justcall(['reboot', '-q'])
  0707010005c95f000081b40000000000000000000000015ab2175400000104000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/rcOsLinux.py   import shutil
import rcOs

class Os(rcOs.Os):
    def reboot(self):
        with open("/proc/sysrq-trigger", "w") as ofile:
            ofile.write("b")

    def crash(self):
        with open("/proc/sysrq-trigger", "w") as ofile:
            ofile.write("c")
0707010005c960000081b40000000000000000000000015ab21754000000b1000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/rcOsSunOS.py   import rcOs
from rcUtilities import justcall

class Os(rcOs.Os):
    def reboot(self):
        justcall(['reboot', '-q'])

    def crash(self):
        justcall(['halt', '-q'])
   0707010005c961000081b40000000000000000000000015ab2175400000e10000000660000000000000000000000000000002400000000root/usr/share/opensvc/lib/rcOvm.py   import os
import subprocess
from rcUtilities import which, call
import rcExceptions as ex
from rcGlobalEnv import rcEnv

class Ovm(object):
    def __init__(self, log=None):
        self.ovmcli = 'ovm'
        if log is not None:
            self.log = log
        else:
            import logging
            self.log = logging.getLogger('OVM')

        import ConfigParser
        cf = rcEnv.paths.authconf
        self.conf = ConfigParser.RawConfigParser()
        self.conf.read(cf)
        if not self.conf.has_section("ovm"):
            raise ex.excError("no auth information for OVM manager")
        if not self.conf.has_option("ovm", "username"):
            raise ex.excError("no username information for OVM manager")
        if not self.conf.has_option("ovm", "password"):
            raise ex.excError("no password information for OVM manager")
        self.username = self.conf.get("ovm", "username")
        self.password = self.conf.get("ovm", "password")

    def test(self):
        if which(self.ovmcli):
            self.log.error("ovm CLI is not installed")
            return False
        if self.username is None:
            self.log.error("manager username is not set")
            return False
        if self.password is None:
            self.log.error("manager password is not set")
            return False
        return True

    def ovm(self, args, check=True, verbose=True):
        if not self.test:
            raise ex.excError
        cmd = [self.ovmcli, '-u', self.username, '-p', self.password, '-S";"'] + args
        if verbose:
            _cmd = [self.ovmcli, '-u', self.username, '-p', 'XXXXXX'] + args
            self.log.info(subprocess.list2cmdline(_cmd))
            ret, out, err = call(cmd, log=self.log)
            if 'Error:' in out > 0:
                self.log.error(out)
            else:
                self.log.info(out)
        else:
            ret, out, err = call(cmd, log=self.log)
        if check and ret != 0:
            raise ex.excError("ovm command execution error")
        return ret, out, err

    def get_pool(self):
        cmd = ['svr', 'ls']
        ret, out, err = self.ovm(cmd, verbose=False)
        for line in out.split('\n'):
            l = line.split(';')
            if len(l) != 4:
                continue
            if l[1].strip('"') == rcEnv.nodename:
                return l[3].strip('"')
        raise ex.excError("can't find node's pool name")

    def vm_enable_ha(self, vm):
        pool = self.get_pool()
        cmd = ['vm', 'conf', '-n', vm, '-s', pool, '-e']
        self.ovm(cmd)

    def vm_disable_ha(self, vm):
        pool = self.get_pool()
        cmd = ['vm', 'conf', '-n', vm, '-s', pool, '-d']
        self.ovm(cmd)

    def vm_info(self, vm):
        pool = self.get_pool()
        cmd = ['vm', 'info', '-n', vm, '-s', pool]
        ret, out, err = self.ovm(cmd, verbose=False)
        if ret != 0:
            raise ex.excError("failed to fetch VM information from manager")
        h = {}
        for line in out.split('\n'):
            l = line.split(':')
            if len(l) != 2:
                continue
            h[l[0].strip()] = l[1].strip()
        return h

    def vm_ha_enabled(self, vm):
        info = self.vm_info(vm)
        if 'Hign Availability' in info and info['Hign Availability'] == 'Enabled':
            return True
        if 'High Availability' in info and info['High Availability'] == 'Enabled':
            return True
        return False

if __name__ == "__main__":
    o = Ovm()
    #o.vm_disable_ha("ovmguest1")
    #o.vm_enable_ha("ovmguest1")
0707010005c962000081b40000000000000000000000015ab217540000015b000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/rcPasswdAIX.py from subprocess import *
from rcUtilities import which

def change_root_pw(pw):
    if which('chpasswd') is not None:
        cmd = ['chpasswd']
        _input = "root:"+pw
    else:
        cmd = ['passwd', '-stdin', 'root']
        _input = pw
    p = Popen(cmd, stdin=PIPE)
    p.stdin.write(_input)
    p.communicate()
    return p.returncode
 0707010005c963000081b40000000000000000000000015ab217540000014b000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/rcPasswdFreeBSD.py from subprocess import *
from rcUtilities import which

def change_root_pw(pw):
    if which('pw') is None:
        print("pw command not found")
        return 1
    cmd = ['pw', 'user', 'mod', 'root', '-h', '0']
    _input = pw
    p = Popen(cmd, stdin=PIPE)
    p.stdin.write(_input)
    p.communicate()
    return p.returncode
 0707010005c964000081b40000000000000000000000015ab2175400000161000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/rcPasswdLinux.py   from subprocess import *
from rcUtilities import which

def change_root_pw(pw):
    if which('chpasswd') is not None:
        cmd = ['chpasswd']
        _input = "root:"+pw+"\n"
    else:
        cmd = ['passwd', '--stdin', 'root']
        _input = pw
    p = Popen(cmd, stdin=PIPE)
    p.stdin.write(_input)
    p.communicate()
    return p.returncode
   0707010005c965000081b40000000000000000000000015ab21754000007b2000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/rcPasswdSunOS.py   from subprocess import *
from rcUtilities import which
import shutil
import crypt

shadow = '/etc/shadow'
tmpshadow = '/etc/shadow.tmp'
policy = '/etc/security/policy.conf'

def get_polices():
    """
       1	crypt_bsdmd5(5)
       2a	crypt_bsdbf(5)
       md5	crypt_sunmd5(5)
       5	crypt_sha256(5)
       6	crypt_sha512(5)
       __unix__	crypt_unix(5)	(default)
    """
    l = ["__unix__"]
    with open(policy, 'r') as fd:
        buff = fd.read()
        for line in buff.split('\n'):
            if line.strip().startswith('CRYPT_ALGORITHMS_ALLOW='):
                v = line.strip().split('=')
                if len(v) != 2:
                    continue
                l = v[1].split(',')
    return l

def change_root_pw(pw):
    try:
        _change_root_pw(pw)
    except Exception as e:
        print(e)
        return 1
    return 0

def _change_root_pw(pw):
    allowed = get_polices()
    if '1' not in allowed:
        raise Exception('can not generate a compatible password')
    shutil.copy(shadow, tmpshadow)
    with open(tmpshadow, 'r') as fd:
        buff = fd.read()
        lines = []
        for line in buff.split('\n'):
            if line.strip().startswith('#'):
                lines.append(line)
                continue
            v = line.split(':')
            if len(v) < 2 or v[0] != "root":
                lines.append(line)
                continue
            v[1] = pw_crypt(pw)
            lines.append(':'.join(v))
        buff = '\n'.join(lines)
    with open(tmpshadow, 'w') as fd:
        fd.write(buff+'\n')
    shutil.copy(tmpshadow, shadow)

def pw_crypt(pw):
    if not which('openssl'):
        raise Exception('openssl is mandatory')
    cmd = ['openssl', 'passwd', '-1', '-stdin']
    p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
    out, err = p.communicate(input=pw)
    if p.returncode != 0:
        raise Exception()
    return out.strip('\n')

if __name__ == "__main__":
    change_root_pw('toto')
  0707010005c966000081b40000000000000000000000015ab2175400002f17000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/rcPgLinux.py   import os
import re
import glob
import rcExceptions as ex
from rcUtilities import justcall
from converters import convert_size

default_cgroup_mntpt = '/cgroup'

def get_cgroup_mntpt(t):
    p = '/proc/mounts'
    if not os.path.exists(p):
        return None
    with open(p, 'r') as f:
        buff = f.read()
    for line in buff.split('\n'):
        if 'cgroup' not in line:
            continue
        l = line.split()
        if len(l) < 6:
            continue
        if l[2] == 'cgroup':
            mntopts = re.split('\W+',l[3])
            for opt in mntopts:
                if t == opt:
                    return l[1]
    return None

def cgroup_capable(res):
    if os.path.exists("/proc/1/cgroup"):
        return True
    kconf = os.path.join(os.sep, 'lib', 'modules',
                         os.uname()[2], 'build', '.config')
    if not os.path.exists(kconf):
        kconf = os.path.join(os.sep, 'boot', 'config-'+os.uname()[2])
    if not os.path.exists(kconf):
        res.log.info("can not detect if system supports process groups")
        return False
    with open(kconf, 'r') as f:
        for line in f.readlines():
            l = line.split('=')
            if len(l) != 2:
                continue
            if l[0] == 'CONFIG_CGROUPS' and l[1] == 'y\n':
                return True
    res.log.info("system does not support process groups")
    return False

def set_task(o, t):
    o.log.debug("set_task : start %s" %(t))
    cgp = get_cgroup_path(o, t)
    path = os.path.join(cgp, "tasks")
    pid = str(os.getpid())
    with open(path, 'r') as f:
        buff = f.read()
    if pid in buff.split():
        return
    try:
        o.log.debug("set_task : open path %s for writing" %(path))
        with open(path, 'w') as f:
            f.write(pid)
    except Exception as e:
        if hasattr(e, "errno") and e.errno == 28:
            # No space left on device
            # means the cgroup has not been initialized with caps yet
            pass
        else:
            raise

def set_cgroup(o, t, name, key, force=False):
    o.log.debug("set_cgroup : start %s, %s, %s, %s" %(t, name, key, force))
    if not hasattr(o, "pg_settings"):
        return
    if key not in o.pg_settings:
        return
    value = o.pg_settings[key]
    cgp = get_cgroup_path(o, t)
    if value is None:
        return
    if not force and get_cgroup(o, t, name).strip() == str(value):
        return
    path = os.path.join(cgp, name)
    if not os.path.exists(path):
        raise ex.excError("can not find %s"%path)
    if hasattr(o, "log"):
        log = o.log
    elif hasattr(o, "svc"):
        log = o.svc.log
    try:
        with open(path, 'w') as f:
            f.write(str(value))
        log.info('/bin/echo %s > %s'%(value, path))
    except Exception as e:
        log.warning("failed to set process group setting %s to %s" % (value, path))

def get_cgroup(o, t, name):
    o.log.debug("get_cgroup : start %s, %s" %(t, name))
    cgp = get_cgroup_path(o, t)
    path = os.path.join(cgp, name)
    if not os.path.exists(path):
        raise ex.excError("can not find %s"%path)
    with open(path, 'r') as f:
        buff = f.read()
    return buff

def set_cpu_quota(o):
    try:
        v = str(o.pg_settings["cpu_quota"])
    except (AttributeError, KeyError):
        return
    o.log.debug("set_cpu_quota : start <%s>", v)

    period = int(get_cgroup(o, 'cpu', 'cpu.cfs_period_us'))

    if "@" in v:
        try:
            quota, cores = v.split("@")
        except Exception as e:
            raise ex.excError("malformed cpu quota: %s (%s)" % (v, str(e)))
    else:
        cores = 1
        quota = v

    if cores == "all":
        import rcAssetLinux
        cores = int(rcAssetLinux.Asset(None)._get_cpu_cores())
    else:
        cores = int(cores)

    total_us = period * cores

    if "%" in quota:
        quota = int(quota.strip("%"))
        tgt_val = total_us * quota // 100
    else:
        tgt_val = int(quota)
    cur_val = int(get_cgroup(o, 'cpu', 'cpu.cfs_quota_us'))

    if tgt_val == cur_val:
        return

    o.pg_settings["cpu_cfs_quota_us"] = tgt_val
    set_cgroup(o, 'cpu', 'cpu.cfs_quota_us', 'cpu_cfs_quota_us')

def set_mem_cgroup(o):
    if not hasattr(o, "pg_settings"):
        return
    o.log.debug("set_mem_cgroup : start <%s>"%(o.pg_settings))

    if 'mem_limit' in o.pg_settings:
        mem_limit = convert_size(o.pg_settings['mem_limit'], _to="", _round=4096)
        o.pg_settings['mem_limit'] = mem_limit
    else:
        mem_limit = None

    if 'vmem_limit' in o.pg_settings:
        vmem_limit = convert_size(o.pg_settings['vmem_limit'], _to="", _round=4096)
        o.pg_settings['vmem_limit'] = vmem_limit
    else:
        vmem_limit = None

    if hasattr(o, "log"):
        log = o.log
    elif hasattr(o, "svc"):
        log = o.svc.log

    #
    # validate memory limits sanity and order adequately the resize
    # depending on increase/decrease of limits
    #
    try:
        cur_vmem_limit = int(get_cgroup(o, 'memory', 'memory.memsw.limit_in_bytes'))
    except ex.excError:
        cur_vmem_limit = None
    if mem_limit is not None and vmem_limit is not None:
        if mem_limit > vmem_limit:
            log.error("pg_vmem_limit must be greater than pg_mem_limit")
            raise ex.excError
        if mem_limit > cur_vmem_limit:
            set_cgroup(o, 'memory', 'memory.memsw.limit_in_bytes', 'vmem_limit')
            set_cgroup(o, 'memory', 'memory.limit_in_bytes', 'mem_limit')
        else:
            set_cgroup(o, 'memory', 'memory.limit_in_bytes', 'mem_limit')
            set_cgroup(o, 'memory', 'memory.memsw.limit_in_bytes', 'vmem_limit')
    elif mem_limit is not None:
        if cur_vmem_limit and mem_limit > cur_vmem_limit:
            log.error("pg_mem_limit must not be greater than current pg_vmem_limit (%d)"%cur_vmem_limit)
            raise ex.excError
        set_cgroup(o, 'memory', 'memory.limit_in_bytes', 'mem_limit')
    elif vmem_limit is not None:
        cur_mem_limit = int(get_cgroup(o, 'memory', 'memory.limit_in_bytes'))
        if vmem_limit < cur_mem_limit:
            log.error("pg_vmem_limit must not be lesser than current pg_mem_limit (%d)"%cur_mem_limit)
            raise ex.excError
        set_cgroup(o, 'memory', 'memory.memsw.limit_in_bytes', 'vmem_limit')

def get_cgroup_path(o, t, create=True):
    o.log.debug("get_cgroup_path : t=%s, create=%s"%(t, create))
    cgroup_mntpt = get_cgroup_mntpt(t)
    if hasattr(o, "svcname"):
        svcname = o.svcname
    else:
        svcname = o.svc.svcname
    if cgroup_mntpt is None:
        raise ex.excError("cgroup fs with option %s is not mounted" % t)

    if hasattr(o, "type") and o.type == "container.lxc" and hasattr(o, "name"):
        cgp = os.path.join(cgroup_mntpt, "lxc", o.name)
    else:
        elements = [cgroup_mntpt, "opensvc", svcname]
        if hasattr(o, "rset") and o.rset is not None:
            elements.append(o.rset.rid.replace(":", "."))
        if hasattr(o, "rid") and o.rid is not None:
            elements.append(o.rid.replace("#", "."))
        cgp = os.path.join(*elements)

    if hasattr(o, "log"):
        log = o.log
    elif hasattr(o, "svc"):
        log = o.svc.log

    if not os.path.exists(cgp) and create:
        if hasattr(o, "cleanup_cgroup"):
            o.cleanup_cgroup(t)
        create_cgroup(cgp, log=log)
    return cgp

def create_cgroup(cgp, log=None):
    log.info("create cgroup %s" % cgp)
    os.makedirs(cgp)
    set_sysfs(cgp+"/cgroup.clone_children", "1", log=log)
    for parm in ("cpus", "mems"):
        parent_val = get_sysfs(cgp+"/../cpuset."+parm)
        set_sysfs(cgp+"/cpuset."+parm, parent_val, log=log)

def get_sysfs(path):
    if not os.path.exists(path):
        return
    with open(path, "r") as f:
        return f.read().rstrip("\n")

def set_sysfs(path, val, log=None):
    current_val = get_sysfs(path)
    if current_val is None:
        return
    if current_val == val:
        return
    
    if log:
        log.info("/bin/echo %s >%s" % (val, path))

    with open(path, "w") as f:
        return f.write(val)

def freeze(o):
    return freezer(o, "FROZEN")

def thaw(o):
    return freezer(o, "THAWED")

def kill(o):
    cgp = get_cgroup_path(o, "freezer")
    pids = set()
    for p in glob.glob(cgp+"/tasks") + glob.glob(cgp+"/*/tasks") + glob.glob(cgp+"/*/*/tasks"):
        with open(p, "r") as f:
            buff = f.read()
        pids |= set(buff.split("\n"))
    pids.remove("")

    if hasattr(o, "log"):
        _o = o
    else:
        _o = o.svc

    if len(pids) == 0:
        _o.log.info("no task to kill")
        return
    cmd = ["kill"] + list(pids)
    _o.vcall(cmd)

    if hasattr(o, "svcname"):
        # lxc containers are not parented to the service cgroup
        # so we have to kill them individually
        for r in o.get_resources("container.lxc"):
            kill(r)


def freezer(o, a):
    if not cgroup_capable(o):
        return
    cgp = get_cgroup_path(o, "freezer")
    _freezer(o, a, cgp)
    if hasattr(o, "svcname"):
        # lxc containers are not parented to the service cgroup
        # so we have to freeze them individually
        for r in o.get_resources("container.lxc"):
            freezer(r, a)

def _freezer(o, a, cgp):
    path = os.path.join(cgp, "freezer.state")
    if hasattr(o, "log"):
        log = o.log
    elif hasattr(o, "svc"):
        log = o.svc.log
    if not os.path.exists(path):
        raise ex.excError("freezer control file not found: %s"%path)
    try:
        with open(path, "r") as f:
            buff = f.read()
    except Exception as e:
        raise ex.excError(str(e))
    buff = buff.strip()
    if buff == a:
        log.info("%s is already %s" % (path, a))
        return
    elif buff == "FREEZING":
        log.info("%s is currently FREEZING" % path)
        return
    try:
        with open(path, "w") as f:
            buff = f.write(a)
    except Exception as e:
        raise ex.excError(str(e))
    log.info("%s on %s submitted successfully" % (a, path))

    # el6 kernel does not freeze child cgroups, as later kernels do
    for _cgp in glob.glob(cgp+"/*/*/freezer.state"):
        _cgp = os.path.dirname(_cgp)
        _freezer(o, a, _cgp)

def get_freeze_state(o):
    if not cgroup_capable(o):
        return False
    cgp = get_cgroup_path(o, "freezer", create=False)
    path = os.path.join(cgp, "freezer.state")
    if not os.path.exists(path):
        return
    with open(path, "r") as f:
        buff = f.read()
    buff = buff.strip()
    return buff

def frozen(res):
    for o in (res.svc, res.rset, res):
        try:
            state = get_freeze_state(o)
        except Exception as e:
            state = None
        if state in ("FROZEN", "FREEZING"):
            try:
                name = o.svcname
            except:
                name = o.rid
            res.status_log("process group %s is %s" % (name, state))
            return True
    return False

def create_pg(res):
    if not cgroup_capable(res):
        return

    _create_pg(res.svc)
    _create_pg(res.rset)
    _create_pg(res)

def _create_pg(o):
    if o is None:
        return
    try:
        try:
            set_task(o, 'systemd')
        except:
            pass
        set_task(o, 'cpu')
        set_task(o, 'cpuset')
        set_task(o, 'memory')
        set_task(o, 'blkio')
        set_task(o, 'freezer')
        set_cgroup(o, 'cpuset', 'cpuset.cpus', 'cpus')
        set_cgroup(o, 'cpu', 'cpu.shares', 'cpu_shares')
        set_cgroup(o, 'cpuset', 'cpuset.mems', 'mems')
        set_cgroup(o, 'blkio', 'blkio.weight', 'blkio_weight')
        set_cgroup(o, 'memory', 'memory.swappiness', 'mem_swappiness')
        set_cgroup(o, 'memory', 'memory.oom_control', 'mem_oom_control')
        set_mem_cgroup(o)
        set_cpu_quota(o)
    except Exception as e:
        try:
            name = o.svcname
        except:
            name = o.rid
        raise ex.excError("process group creation in '%s' cgroup failed: %s"%(name, str(e)))
 0707010005c967000081b40000000000000000000000015ab21754000001fc000000660000000000000000000000000000002700000000root/usr/share/opensvc/lib/rcPkgAIX.py    import os
from rcUtilities import justcall, which
from rcGlobalEnv import rcEnv

def listpkg():
    cmd = ['lslpp', '-Lc']
    out, err, ret = justcall(cmd)
    if ret != 0:
        return []
    lines = []
    for line in out.split('\n'):
        l = line.split(':')
        if len(l) < 5:
            continue
        pkgvers = l[2]
        pkgname = l[1].replace('-'+pkgvers, '')
        x = [rcEnv.nodename, pkgname, pkgvers, '']
        lines.append(x)
    return lines

def listpatch():
    return []

0707010005c968000081b40000000000000000000000015ab21754000003e1000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/rcPkgDarwin.py import os
from rcUtilities import call, which
from rcGlobalEnv import rcEnv

"""
format:

package-id: com.apple.pkg.X11User
version: 10.6.0.1.1.1238328574
volume: /
location: /
install-time: 1285389505
groups: com.apple.snowleopard-repair-permissions.pkg-group com.apple.FindSystemFiles.pkg-group
"""

def pkgversion(package):
    cmd = ['pkgutil', '--pkg-info', package]
    (ret, out, err) = call(cmd, errlog=False, cache=True)
    for line in out.split('\n'):
        l = line.split(': ')
        if len(l) != 2:
            continue
        if l[0] == 'version':
            return l[1]
    return ''

def listpkg():
    if which('pkgutil') is None:
        return []
    cmd = ['pkgutil', '--packages']
    (ret, out, err) = call(cmd, errlog=False, cache=True)
    lines = []
    for line in out.split('\n'):
        if len(line) == 0:
            continue
        x = [rcEnv.nodename, line, pkgversion(line), ""]
        lines.append(x)
    return lines

def listpatch():
    return []

   0707010005c969000081b40000000000000000000000015ab21754000003fb000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/rcPkgFreeBSD.py    from rcUtilities import justcall, which, cache
from rcGlobalEnv import rcEnv

def listpkg():
    lines = list_pkg_info()
    lines += list_pkg_query()
    return lines

@cache("pkg_info")
def list_pkg_info():
    if which('pkg_info') is None:
        return []
    cmd = ['pkg_info']
    out, err, ret = justcall(cmd)
    lines = []
    for line in out.splitlines():
        l = line.split()
        if len(l) < 2:
            continue
        nv = l[0].split('-')
        version = nv[-1]
        pkgname = '-'.join(nv[0:-1])
        x = [rcEnv.nodename, pkgname, version, '']
        lines.append(x)
    return lines

@cache("pkg_query")
def list_pkg_query():
    if which('pkg') is None:
        return []
    cmd = ['pkg', 'query', '-a', '%n;;%v;;%q']
    out, err, ret = justcall(cmd)
    lines = []
    for line in out.splitlines():
        l = line.split(';;')
        if len(l) < 3:
            continue
        x = [rcEnv.nodename] + l
        lines.append(x)
    return lines

def listpatch():
    return []

 0707010005c971000081b40000000000000000000000015ab2175400000d58000000660000000000000000000000000000002600000000root/usr/share/opensvc/lib/rcStats.py import os
import datetime

class StatsProvider(object):
    def __init__(self, interval=2880, stats_dir=None, stats_start=None, stats_end=None):
        self.stats_dir = stats_dir
        self.interval = interval

        if stats_end is None:
            self.stats_end = datetime.datetime.now()
        else:
            self.stats_end = datetime.datetime.strptime(stats_end,"%Y-%m-%d %H:%M")

        if stats_start is None:
            self.stats_start = self.stats_end - datetime.timedelta(minutes=interval)
        else:
            self.stats_start = datetime.datetime.strptime(stats_start,"%Y-%m-%d %H:%M")
            delta = self.stats_end - self.stats_start
            interval = delta.days * 1440 + delta.seconds // 60

        # discard seconds
        self.stats_start -= datetime.timedelta(seconds=self.stats_start.second)
        self.stats_end -= datetime.timedelta(seconds=self.stats_end.second)

        x, self.nodename, x, x, x = os.uname()

        self.minutes_first_day = 60*self.stats_end.hour + self.stats_end.minute + 1

        one_minute = datetime.timedelta(minutes=1)
        one_day = datetime.timedelta(days=1)
        self.ranges = []
        i = 0
        end = self.stats_end

        while end > self.stats_start:
            start = end - one_day
            if start < self.stats_start:
                start = self.stats_start
            if start.day != end.day:
                start = end - datetime.timedelta(hours=end.hour, minutes=end.minute)
            if start != end:
                self.ranges.append((start, end))
            end = start - one_minute
        #print(self.stats_end, interval, [x.strftime("%Y-%m-%d %H:%M:%S")+" - "+y.strftime("%Y-%m-%d %H:%M:%S") for x, y in self.ranges])

    def get(self, fname):
        lines = []
        cols = []
        if not hasattr(self, fname):
            print(fname, 'is not implemented')
            return cols, lines
        for start, end in self.ranges:
            date = start.strftime("%Y-%m-%d")
            day = start.strftime("%d")
            start = start.strftime("%H:%M:%S")
            end = end.strftime("%H:%M:%S")
            _cols, _lines = getattr(self, fname)(date, day, start, end)
            if len(_cols) == 0 or len(_lines) == 0:
                continue
            cols = _cols
            lines += _lines
        return cols, lines

    def sarfile(self, day):
        if self.stats_dir is None:
            stats_dir = os.path.join(os.sep, 'var', 'log', 'sysstat')
            if not os.path.exists(stats_dir):
                stats_dir = os.path.join(os.sep, 'var', 'log', 'sa')
        else:
            stats_dir = self.stats_dir
        f = os.path.join(stats_dir, 'sa'+day)
        if os.path.exists(f):
            return f
        return None

    def cpu(self, d, day, start, end):
        return [], []

    def mem_u(self, d, day, start, end):
        return [], []

    def proc(self, d, day, start, end):
        return [], []

    def swap(self, d, day, start, end):
        return [], []

    def block(self, d, day, start, end):
        return [], []

    def blockdev(self, d, day, start, end):
        return [], []

    def netdev(self, d, day, start, end):
        return [], []

    def netdev_err(self, d, day, start, end):
        return [], []

if __name__ == "__main__":
    sp = StatsProvider(interval=20)
    print(sp.get('cpu'))
    print(sp.get('swap'))
0707010005c96a000081b40000000000000000000000015ab2175400000338000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/rcPkgHP-UX.py  from rcUtilities import call, which
from rcGlobalEnv import rcEnv
import datetime

def listpkg():
    if which('swlist') is None:
        return []
    lines = []
    for t in ('product', 'bundle'):
        lines += listpkg_t(t)
    return lines

def listpkg_t(t):
    cmd = ['swlist', '-l', t, '-a', 'revision', '-a', 'mod_time']
    (ret, out, err) = call(cmd, errlog=False, cache=True)
    lines = []
    for line in out.split('\n'):
        l = line.split()
        if len(l) < 3:
            continue
        if line[0] == '#':
            continue
        try:
            l[2] = datetime.datetime.fromtimestamp(int(l[2])).strftime("%Y-%m-%d %H:%M:%S")
        except:
            l[2] = ""
        x = [rcEnv.nodename, l[0], l[1], '', t, l[2]]
        lines.append(x)
    return lines

def listpatch():
    return []
0707010005c96b000081b40000000000000000000000015ab217540000069a000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/rcPkgLinux.py  import os
from rcUtilities import call, which
from rcGlobalEnv import rcEnv
import datetime
from stat import *

def listpkg_dummy():
    print("pushpkg not supported on this system")
    return []

def listpkg_rpm():
    (ret, out, err) = call(cmd, errlog=False, cache=True)
    lines = []
    for line in out.split('\n'):
        if line.startswith('Signature'):
            sig = line.split()[-1].strip()
            continue
        elif not line.startswith('XX'):
            continue
        line = line[2:]
        l = line.split()
        if len(l) < 5:
            continue
        try:
            l[4] = datetime.datetime.fromtimestamp(int(l[4])).strftime("%Y-%m-%d %H:%M:%S")
        except:
            l[4] = ""
        x = [rcEnv.nodename] + l + [sig]
        lines.append(x)
    return lines

def listpkg_deb():
    (ret, out, err) = call(cmd, errlog=False, cache=True)
    lines = []
    arch = ""
    for line in out.splitlines():
        l = line.split()
        if len(l) < 4:
            continue
        if l[0] != "ii":
            continue
        x = [rcEnv.nodename] + l[1:3] + [arch, "deb"]
        try:
            t = os.stat("/var/lib/dpkg/info/"+l[1]+".list")[ST_MTIME]
            t = datetime.datetime.fromtimestamp(t).strftime("%Y-%m-%d %H:%M:%S")
        except:
            t = ""
        x.append(t)
        lines.append(x)
    return lines

if which('dpkg') is not None:
    cmd = ['dpkg', '-l']
    listpkg = listpkg_deb
elif which('rpm') is not None:
    cmd = ['rpm', '-qai', '--queryformat=XX%{n} %{v}-%{r} %{arch} rpm %{installtime}\n']
    listpkg = listpkg_rpm
else:
    cmd = ['true']
    listpkg = listpkg_dummy

def listpatch():
    return []
  0707010005c96c000081b40000000000000000000000015ab217540000051e000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/rcPkgOSF1.py   from rcUtilities import justcall
from rcGlobalEnv import rcEnv

"""
Subset               Status                 Description
------               ------                 -----------
IOSFRBASE540         installed              French Base System (French Support - Operating System)
IOSFRCDEHLP540       not installed          French CDE Online Help (French Support - Windowing Environment)
IOSFRCDEMIN540       installed              French CDE Minimum Runtime Environment(French Support - Windowing Environment)
IOSFRX11540          installed              French Basic X Environment (French Support - Windowing Environment)
"""

def _list():
    cmd = ['setld', '-i']
    out, err, ret = justcall(cmd)
    pkg = []
    patch = []
    pkgarch = ""
    pkgvers = ""
    if ret != 0:
        return []
    lines = out.split('\n')
    if len(lines) < 3:
        return []
    for line in lines[2:]:
        if "installed" not in line or "not installed" in line:
            continue
        name = line.split()[0]
        if "Patch:" in line:
            x = [rcEnv.nodename, name, pkgvers]
            patch.append(x)
        else:
            x = [rcEnv.nodename, name, pkgvers, pkgarch]
            pkg.append(x)
    return pkg, patch

def listpkg():
    return _list()[0]

def listpatch():
    return _list()[1]
  0707010005c96d000081b40000000000000000000000015ab2175400000b02000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/rcPkgSunOS.py  import os
import datetime
from rcUtilities import call, which
from rcGlobalEnv import rcEnv
from stat import *

"""
format:

   PKGINST:  SUNWzoneu
      NAME:  Solaris Zones (Usr)
  CATEGORY:  system
      ARCH:  i386
   VERSION:  11.11,REV=2009.04.08.17.26
    VENDOR:  Sun Microsystems, Inc.
      DESC:  Solaris Zones Configuration and Administration
   HOTLINE:  Please contact your local service provider
    STATUS:  completely installed

"""

def listpkg_ips():
    if which('uname') is None:
        return []
    cmd = ['uname', '-p']
    (ret, out, err) = call(cmd, errlog=False, cache=True)
    arc = out.split('\n')[0]
    if which('pkg') is None:
        return []
    cmd = ['pkg', 'list', '-H']
    (ret, out, err) = call(cmd, errlog=False, cache=True)
    lines = []
    for line in out.split('\n'):
        l = line.split()
        if len(l) != 3:
            continue
        x = [rcEnv.nodename, l[0], l[1], arc, "ips", ""]
        lines.append(x)
    return lines

def listpkg_legacy():
    if which('pkginfo') is None:
        return []
    cmd = ['pkginfo', '-l']
    (ret, out, err) = call(cmd, errlog=False, cache=True)
    lines = []
    for line in out.split('\n'):
        l = line.split(':')
        if len(l) != 2:
            continue
        l = map(lambda x: x.strip(), l)
        f = l[0]
        if f == "PKGINST":
            x = [rcEnv.nodename, l[1], "", "", "pkg", ""]
        elif f == "VERSION":
            x[2] = l[1]
        elif f == "ARCH":
            x[3] = l[1]
            lines.append(x)

    for i, x in enumerate(lines):
        # pkg install date
        try:
            t = os.stat("/var/sadm/pkg/"+x[1])[ST_MTIME]
            t = datetime.datetime.fromtimestamp(t).strftime("%Y-%m-%d %H:%M:%S")
        except Exception as e:
            t = ""
        lines[i][5] = t

    return lines

def listpkg():
    return listpkg_legacy() + listpkg_ips()

def listpatch():
    """
    Patch: patchnum-rev Obsoletes: num-rev[,patch-rev]... Requires: .... Incompatibles: ... Packages: ...
    """
    if which('showrev') is None:
        return []
    cmd = ['showrev', '-p']
    (ret, out, err) = call(cmd, errlog=False, cache=True)
    lines = []
    nodename = rcEnv.nodename
    for line in out.split('\n'):
        l = line.split(' ')
        if len(l) > 3:
            p = l[1].split('-')
            if len(p) != 2:
                continue
            else:
                lines.append( [ nodename , p[0], p[1] ] )

    for i, x in enumerate(lines):
        # pkg install date
        try:
            t = os.stat("/var/sadm/patch/"+x[1])[ST_MTIME]
            t = datetime.datetime.fromtimestamp(t).strftime("%Y-%m-%d %H:%M:%S")
        except:
            t = ""
        lines[i].append(t)

    return lines

if __name__ == "__main__" :
    print(listpatch())
  0707010005c96e000081b40000000000000000000000015ab21754000011b4000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/rcPkgWindows.py    from collections import namedtuple
from ctypes import byref, create_unicode_buffer, windll
from ctypes.wintypes import DWORD
from itertools import count

# defined at http://msdn.microsoft.com/en-us/library/aa370101(v=VS.85).aspx
UID_BUFFER_SIZE = 39
PROPERTY_BUFFER_SIZE = 256
ERROR_MORE_DATA = 234
ERROR_INVALID_PARAMETER = 87
ERROR_SUCCESS = 0
ERROR_NO_MORE_ITEMS = 259
ERROR_UNKNOWN_PRODUCT = 1605

# diff propoerties of a product, not all products have all properties
PRODUCT_PROPERTIES = [u'Language',
                      u'ProductName',
                      u'PackageCode',
                      u'Transforms',
                      u'AssignmentType',
                      u'PackageName',
                      u'InstalledProductName',
                      u'VersionString',
                      u'RegCompany',
                      u'RegOwner',
                      u'ProductID',
                      u'ProductIcon',
                      u'InstallLocation',
                      u'InstallSource',
                      u'InstallDate',
                      u'Publisher',
                      u'LocalPackage',
                      u'HelpLink',
                      u'HelpTelephone',
                      u'URLInfoAbout',
                      u'URLUpdateInfo',]

# class to be used for python users :)
Product = namedtuple('Product', PRODUCT_PROPERTIES)

def get_property_for_product(product, property, buf_size=PROPERTY_BUFFER_SIZE):
    """Retruns the value of a fiven property from a product."""
    property_buffer = create_unicode_buffer(buf_size)
    size = DWORD(buf_size)
    result = windll.msi.MsiGetProductInfoW(product, property, property_buffer,
                                           byref(size))
    if result == ERROR_MORE_DATA:
        return get_property_for_product(product, property,
                                        2 * buf_size)
    elif result == ERROR_SUCCESS:
        return property_buffer.value
    else:
        return None

def populate_product(uid):
    """Return a Product with the different present data."""
    properties = []
    for property in PRODUCT_PROPERTIES:
        properties.append(get_property_for_product(uid, property))
    return Product(*properties)

def get_installed_products_uids():
    """Returns a list with all the different uid of the installed apps."""
    # enum will return an error code according to the result of the app
    products = []
    for i in count(0):
        uid_buffer = create_unicode_buffer(UID_BUFFER_SIZE)
        result = windll.msi.MsiEnumProductsW(i, uid_buffer)
        if result == ERROR_NO_MORE_ITEMS:
            # done interating over the collection
            break
        products.append(uid_buffer.value)
    return products

def get_installed_products():
    """Returns a collection of products that are installed in the system."""
    products = []
    for puid in  get_installed_products_uids():
        products.append(populate_product(puid))
    return products

def is_product_installed_uid(uid):
    """Return if a product with the given id is installed.

    uid Most be a unicode object with the uid of the product using
    the following format {uid}
    """
    # we try to get the VersisonString for the uid, if we get an error it means
    # that the product is not installed in the system.
    buf_size = 256
    uid_buffer = create_unicode_buffer(uid)
    property = u'VersionString'
    property_buffer = create_unicode_buffer(buf_size)
    size = DWORD(buf_size)
    result = windll.msi.MsiGetProductInfoW(uid_buffer, property, property_buffer,
                                           byref(size))
    if result == ERROR_UNKNOWN_PRODUCT:
        return False
    else:
        return True

from rcGlobalEnv import rcEnv

def listpkg():
    plist = get_installed_products()
    lines = []
    for p in plist:
        x = [rcEnv.nodename,
             p.ProductName,
             p.VersionString,
             "",
	     "msi",
             p.InstallDate]
        lines.append(x)
    return lines


def listpatch():
    import wmi
    import datetime
    wmi = wmi.WMI()
    lines = []
    for hotfix in wmi.WIN32_QuickFixEngineering():
        try:
            t = hotfix.InstalledOn
            t = datetime.datetime.strptime(t, "%m/%d/%Y").strftime("%Y-%m-%d")
        except:
            t = ""
        lines.append([
          rcEnv.nodename,
          hotfix.HotFixID,
          "",
          t
        ])
    return lines

if __name__ == "__main__":
    print(get_installed_products())
0707010005c96f000081b40000000000000000000000015ab2175400001110000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/rcPrintTable.py    from __future__ import print_function
from __future__ import unicode_literals

import sys

from textwrap import wrap
from rcColor import color, colorize
from rcUtilities import term_width
import rcExceptions as ex

if sys.version_info[0] >= 3:
    from functools import reduce
    unicode = str

def parse_data(data):
    try:
        lines = data.splitlines()
    except AttributeError:
        raise ex.excError
    if len(lines) < 2:
        return []
    labels = list(map(lambda x: x.split('.')[-1], lines[0].split(',')))
    lines = lines[1:]
    rows = []
    for line in lines:
        row = []
        incell = False
        cell_begin = 0
        l = len(line)
        for i, c in enumerate(line):
            if c != ',' and i < l-1:
                continue
            if incell and ((i>1 and line[i-1] == '"') or i == l-1):
                incell = False
            if not incell:
                if i > 0:
                    if i < l-1:
                        cell = line[cell_begin:i].replace('""', '"')
                    else:
                        cell = line[cell_begin:].replace('""', '"')
                else:
                    cell = ""
                if len(cell) > 1 and cell[0] == '"' and cell[-1] == '"':
                    if len(cell) > 2:
                        cell = cell[1:-1]
                    else:
                        cell = ""
                row.append(cell)
                cell_begin = i+1
                if i<l-1 and line[i+1] == '"':
                    incell = True
        rows.append(row)
    return [labels]+rows

def convert(s):
    try:
        return unicode(s)
    except:
        pass
    try:
        return unicode(s, errors="ignore")
    except:
        pass
    try:
        return str(s)
    except:
        pass
    return s

def validate_format(data):
    if data is None:
        raise Exception

    if not isinstance(data, list):
        data = parse_data(data)

    if len(data) == 0:
        raise Exception

    if not isinstance(data[0], list):
        for s in data:
            print(s)
        raise Exception

    if len(data) < 2:
        raise Exception

    return data

def print_table_tabulate(data, width=20):
    try:
        data = validate_format(data)
    except Exception as e:
        return

    from tabulate import tabulate
    import rcColor

    try:
        table = tabulate(data, headers="firstrow", tablefmt="plain").splitlines()
    except UnicodeEncodeError:
        table = tabulate(data, headers="firstrow", tablefmt="plain").encode("utf-8").splitlines()

    colors = [
        rcColor.color.BGWHITE+rcColor.color.BLACK,
        rcColor.color.E_BGODD+rcColor.color.BLACK,
    ]
    idx = 0
    tw = term_width()

    for line_idx, line in enumerate(table):
        if line.startswith("+-"):
            idx = (idx + 1) % 2
            continue
        if line_idx == 0:
            color = rcColor.color.BOLD
        else:
            color = colors[idx]
        if line.endswith("|"):
            cont = False
        else:
            cont = True
        length = len(line)
        if length > tw or cont:
            rpad = tw - (length % tw)
            line += " " * rpad
        print(rcColor.colorize(line, color))

def print_table_default(data):
    try:
        data = validate_format(data)
    except Exception as e:
        return

    labels = data[0]
    max_label_len = reduce(lambda x,y: max(x,len(y)), labels, 0)+1
    data = data[1:]
    subsequent_indent = ""
    for i in range(max_label_len+3):
        subsequent_indent += " "
    fmt = " %-"+str(max_label_len)+"s "
    for j, d in enumerate(data):
        print("-")
        for i, label in enumerate(labels):
            val = '\n'.join(wrap(convert(d[i]),
                       initial_indent = "",
                       subsequent_indent = subsequent_indent,
                       width=78
                  ))
            try:
                print(colorize(fmt % (label+":"), color.LIGHTBLUE), val)
            except UnicodeEncodeError:
                print(colorize(fmt % (label+":"), color.LIGHTBLUE), val.encode("utf-8"))

def print_table_csv(data):
    try:
        data = validate_format(data)
    except ex.excError:
        raise ex.excError("unsupported format for this action")

    for d in data:
        print(";".join(map(lambda x: "'%s'" % unicode(x), d)))

0707010005c970000081b40000000000000000000000015ab217540000b181000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/rcScheduler.py """
This module defines the Scheduler class inherited by Svc and Node.
"""
from __future__ import print_function
import sys
import os
import datetime
import json
import time
import random
import logging

import lock
import rcExceptions as ex
from rcGlobalEnv import rcEnv, Storage
from rcUtilities import is_string
from rcColor import formatter
from converters import convert_duration

SCHED_FMT = "%s: %s"
CALENDAR_NAMES = {
    "jan": 1,
    "feb": 2,
    "mar": 3,
    "apr": 4,
    "may": 5,
    "jun": 6,
    "jul": 7,
    "aug": 8,
    "sep": 9,
    "oct": 10,
    "nov": 11,
    "dec": 12,
    "january": 1,
    "february": 2,
    "march": 3,
    "april": 4,
    "june": 6,
    "july": 7,
    "august": 8,
    "september": 9,
    "october": 10,
    "november": 11,
    "december": 12,
    "mon": 0,
    "tue": 1,
    "wed": 2,
    "thu": 3,
    "fri": 4,
    "sat": 5,
    "sun": 6,
    "monday": 0,
    "tuesday": 1,
    "wednesday": 2,
    "thursday": 3,
    "friday": 4,
    "saturday": 5,
    "sunday": 6,
}

class SchedNotAllowed(Exception):
    """
    The exception signaling the task can not run due to scheduling
    constaints.
    """
    pass

class SchedNoDefault(Exception):
    """
    The exception raised to signal a task has no default schedule
    defined.
    """
    pass

class SchedSyntaxError(Exception):
    """
    The exception raised to signal the defined schedule has syntax
    errors.
    """
    pass

class SchedExcluded(Exception):
    """
    The exception raised to signal a negative constraint violation.
    """
    pass

class SchedOpts(object):
    """
    The class storing a task schedule options.
    """
    def __init__(self, section,
                 fname=None,
                 schedule_option="push_schedule"):
        self.section = section
        self.fname = fname
        if self.fname is None:
            self.fname = "last_"+section+"_push"
        self.schedule_option = schedule_option

def sched_action(func):
    """
    A decorator in charge of locking and updating the scheduler tasks
    and subtasks timestamps.
    """
    def _func(self, action, options=None):
        fds = []
        if options is None:
            options = Storage()
        try:
            if action in self.sched.scheduler_actions:
                fds = self.sched.action_timestamps(action, options.rid)
            ret = func(self, action, options)
            if ret == 0 and action in self.sched.scheduler_actions:
                self.sched.action_timestamps(action, options.rid, success=True)
            return ret
        finally:
            for fd in fds:
                lock.unlock(fd)
    return _func

class Scheduler(object):
    """
    The scheduler class.

    The node and each service inherit an independent scheduler through
    this class.
    """
    def __init__(self, config_defaults=None, config=None, options=None,
                 scheduler_actions=None, log=None, name="node", svc=None):
        self.config_defaults = config_defaults
        self.config = config

        if scheduler_actions is None:
            self.scheduler_actions = {}
        else:
            self.scheduler_actions = scheduler_actions

        if options is None:
            self.options = Storage()
        else:
            self.options = options

        self.name = name
        self.svc = svc
        self.log = log
        self.sched_log_shut = False

    def sched_log(self, task, msg, level):
        """
        A logger wrapping method, used to log to the service or node
        sublogger dedicated to scheduling.
        """
        if self.sched_log_shut:
            return

        try:
            task = task.replace(self.name + ".", "")
        except:
            pass

        log = logging.getLogger(self.log.name+".scheduler")
        getattr(log, level)(SCHED_FMT % (task, msg))

    def get_next_schedule(self, action, _max=14400):
        """
        Iterate future dates in search for the next date validating
        <action> scheduling constraints.
        """
        self.sched_log_shut = True
        now = datetime.datetime.now()
        cron = self.options.cron
        self.options.cron = True
        for idx in range(_max):
            future_dt = now + datetime.timedelta(minutes=idx*10)
            data = self.skip_action(action, now=future_dt)
            if isinstance(data, dict):
                if len(data["keep"]) > 0:
                    return {"next_sched": future_dt, "minutes": _max}
            elif not data:
                self.options.cron = cron
                return {"next_sched": future_dt, "minutes": _max}
        self.options.cron = cron
        return {"next_sched": None, "minutes": None}

    @staticmethod
    def _need_action_interval(last, delay=10, now=None):
        """
        Return False if timestamp is fresher than now-interval
        Return True otherwize.
        Zero is a infinite interval.
        """
        if delay == 0:
            return False
        if last is None:
            return True
        if now is None:
            now = datetime.datetime.now()
        limit = last + datetime.timedelta(minutes=delay)
        if now < limit:
            return False
        else:
            return True

        # never reach here
        return True

    def sched_write_timestamp(self, sopt):
        """
        Iterate the scheduled tasks to update the last run timestamp.
        """
        if not isinstance(sopt, list):
            sopt = [sopt]
        for _sopt in sopt:
            self._timestamp(_sopt.fname)

    def _timestamp(self, timestamp_f):
        """
        Update the timestamp file <timestamp_f>.
        If <timestamp_f> if is not a fullpath, consider it parented to
        <pathvar>.
        Create missing parent directories if needed.
        """
        if not timestamp_f.startswith(os.sep):
            timestamp_f = self.get_timestamp_f(timestamp_f)
        timestamp_d = os.path.dirname(timestamp_f)
        if not os.path.isdir(timestamp_d):
            os.makedirs(timestamp_d, 0o755)
        with open(timestamp_f, 'w') as ofile:
            ofile.write(str(datetime.datetime.now())+os.linesep)
        return True

    def _skip_action_interval(self, last, interval, now=None):
        """
        Return the negation of _need_action_interval()
        """
        return not self._need_action_interval(last, interval, now=now)

    def _timerange_delay(self, timerange, now=None):
        """
        Return a delay in seconds, compatible with the timerange.

        The daemon scheduler thread will honor this delay,
        executing the task only when expired.

        This algo is meant to level collector's load which peaks
        when tasks trigger at the same minute on every nodes.
        """
        if not timerange.get("probabilistic", False):
            return 0

        try:
            begin = self._time_to_minutes(timerange["begin"])
            end = self._time_to_minutes(timerange["end"])
            now = self._time_to_minutes(now)
        except:
            raise SchedNotAllowed("time conversion error delay eval")

        # day change in the timerange
        if begin > end:
            end += 1440
        if now < begin:
            now += 1440

        length = end - begin
        remaining = end - now - 1

        if remaining < 10:
            # no need to delay for tasks with a short remaining valid time
            return 0

        if timerange["interval"] < length:
            # don't delay if interval < period length, because the user
            # expects the action to run multiple times in the period. And
            # '@<n>' interval-only schedule are already different across
            # nodes due to daemons not starting at the same moment.
            return 0

        rnd = random.random()

        return int(remaining*60*rnd)

    @staticmethod
    def _time_to_minutes(dt_spec):
        """
        Convert a datetime or a %H:%M formatted string to minutes.
        """
        if isinstance(dt_spec, datetime.datetime):
            dtm = dt_spec
            dt_spec = dtm.hour * 60 + dtm.minute
        else:
            try:
                dtm = time.strptime(dt_spec, "%H:%M")
            except:
                raise Exception("malformed time string: %s"%str(dt_spec))
            dt_spec = dtm.tm_hour * 60 + dtm.tm_min
        return dt_spec

    def _in_timeranges(self, schedule, fname=None, now=None, last=None):
        """
        Validate the timerange constraints of a schedule.
        Iterates multiple allowed timeranges.

        Return a delay the caller should wait before executing the task,
        with garanty the delay doesn't reach outside the valid timerange:

        * 0 => immediate execution
        * n => seconds to wait

        Raises SchedNotAllowed if the validation fails the timerange
        constraints.
        """
        if len(schedule["timeranges"]) == 0:
            raise SchedNotAllowed("no timeranges")
        errors = []
        delay = 0
        for timerange in schedule["timeranges"]:
            try:
                self.in_timerange(timerange, now=now)
                self.in_timerange_interval(timerange, fname=fname, now=now, last=last)
                if fname is not None:
                    # fname as None indicates we run in test mode
                    delay = self._timerange_delay(timerange, now=now)
                return delay
            except SchedNotAllowed as exc:
                errors.append(str(exc))
        raise SchedNotAllowed(", ".join(errors))

    def in_timerange(self, timerange, now=None):
        """
        Validate if <now> is in <timerange>.
        """
        try:
            begin = self._time_to_minutes(timerange["begin"])
            end = self._time_to_minutes(timerange["end"])
            now = self._time_to_minutes(now)
        except:
            raise SchedNotAllowed("conversion error in timerange challenge")

        if begin <= end:
            if now >= begin and now <= end:
                return
        elif begin > end:
            #
            #     =================
            #     23h     0h      1h
            #
            if (now >= begin and now <= 1440) or \
               (now >= 0 and now <= end):
                return
        raise SchedNotAllowed("not in timerange %s-%s" % \
                              (timerange["begin"], timerange["end"]))

    def get_last(self, fname):
        """
        Return the last task run timestamp, fetched from the on-disk cache.
        """
        timestamp_f = self.get_timestamp_f(fname)
        if not os.path.exists(timestamp_f):
            return
        try:
            with open(timestamp_f, 'r') as ofile:
                buff = ofile.read()
            last = datetime.datetime.strptime(buff, "%Y-%m-%d %H:%M:%S.%f"+os.linesep)
            return last
        except (OSError, IOError, ValueError):
            return

    def in_timerange_interval(self, timerange, fname=None, now=None, last=None):
        """
        Validate if the last task run is old enough to allow running again.
        """
        if timerange["interval"] == 0:
            raise SchedNotAllowed("interval set to 0")
        if fname and last is None:
            last = self.get_last(fname)
        if last is None:
            return
        if self._skip_action_interval(last, timerange["interval"], now=now):
            raise SchedNotAllowed("last run is too soon")
        return

    def _in_schedule(self, schedule, fname=None, now=None, last=None):
        """
        Validate if <now> is in the allowed days and in the allowed timranges.
        """
        self._in_days(schedule, now=now)
        delay = self._in_timeranges(schedule, fname=fname, now=now, last=last)
        return delay

    def in_schedule(self, schedules, fname=None, now=None, last=None):
        """
        Validate if <now> pass the constraints of a set of schedules,
        iterating over each non-excluded one.
        """
        if len(schedules) == 0:
            raise SchedNotAllowed("no schedule")
        errors = []
        for schedule in schedules:
            try:
                delay = self._in_schedule(schedule, fname=fname, now=now, last=last)
                if schedule["exclude"]:
                    raise SchedExcluded('excluded by schedule member "%s"' % schedule["raw"])
                else:
                    return delay
            except SchedNotAllowed as exc:
                errors.append(str(exc))
        raise SchedNotAllowed(", ".join(errors))

    def sched_convert_to_schedule(self, config, section, prefix=""):
        """
        Read and convert a deprecated schedule definition from a configuration
        file section, handle json-formatted lists, and finally return a
        current-style schedule string.
        """
        def get_val(param):
            if not config.has_section(section) or \
               (not config.has_option(section, param) and \
                not config.has_option(section, prefix+param)):
                # internal syncs
                config_defaults = config.defaults()
                val = config_defaults.get(prefix+param)
            elif config.has_option(section, prefix+param):
                val = config.get(section, prefix+param)
            else:
                val = config.get(section, param)
            return str(val)

        days_s = get_val("days")
        interval_s = get_val("interval")
        period_s = get_val("period")

        if days_s == "None" or interval_s == "None" or period_s == "None":
            return ""

        try:
            days = json.loads(days_s)
        except:
            self.log.error("invalid days schedule definition in section",
                           section, days_s, file=sys.stderr)
            return ""

        try:
            periods = json.loads(period_s)
            elements = []
            if is_string(periods[0]):
                periods = [periods]
            for period in periods:
                elements.append("%s-%s@%s" % (period[0], period[1], interval_s))
            period_s = ",".join(elements)
        except:
            self.log.error("invalid periods schedule definition in section",
                           section, file=sys.stderr)
            return ""
        buff = "%(period)s %(days)s" % dict(
            period=period_s,
            days=",".join(days),
        )
        return buff.strip()

    def sched_get_schedule_raw(self, section, option):
        """
        Read the old/new style schedule options of a configuration file
        section. Convert if necessary and return the new-style formatted
        string.
        """
        if option is None:
            raise SchedNoDefault

        config = self.config

        def has_old_schedule_options(config, section):
            """
            Return True if a configuration file section has a deprecated
            schedule definition keyword
            """
            if config.has_option(section, 'sync_days') or \
               config.has_option(section, 'sync_interval') or \
               config.has_option(section, 'sync_period'):
                return True
            if config.has_option(section, 'days') or \
               config.has_option(section, 'interval') or \
               config.has_option(section, 'period'):
                return True
            return False

        if config.has_section(section) and \
           config.has_option(section, 'schedule'):
            schedule_s = config.get(section, 'schedule')
        elif section.startswith("sync") and config.has_section(section) and \
             has_old_schedule_options(config, section):
            if section.startswith("sync"):
                prefix = "sync_"
            elif section.startswith("app"):
                prefix = "app_"
            else:
                prefix = ""
            schedule_s = self.sched_convert_to_schedule(config, section, prefix=prefix)
        elif section.startswith("sync") and not config.has_section(section) and (\
              'sync_days' in config.defaults() or \
              'sync_interval' in config.defaults() or \
              'sync_period' in config.defaults() \
             ):
            schedule_s = self.sched_convert_to_schedule(config, section, prefix="sync_")
        elif config.has_option('DEFAULT', option):
            schedule_s = config.get('DEFAULT', option)
        elif self.svc and section in self.svc.resources_by_id and \
             hasattr(self.svc.resources_by_id[section], "default_schedule"):
            schedule_s = self.svc.resources_by_id[section].default_schedule
        elif option in self.config_defaults:
            schedule_s = self.config_defaults[option]
        else:
            raise SchedNoDefault

        return schedule_s

    def _in_days(self, schedule, now=None):
        self._sched_validate_month(schedule["month"], now=now)
        self._sched_validate_week(schedule["week"], now=now)
        self._sched_validate_day(schedule["day"], now=now)

    def _sched_validate_day(self, day, now=None):
        """
        Split the allowed <day> spec and for each element,
        validate if <now> is in allowed <day> of week and of month.
        """
        for _day in day.split(","):
            try:
                self.__sched_validate_day(_day, now=now)
                return
            except SchedNotAllowed:
                pass
        raise SchedNotAllowed("not in allowed days")

    def __sched_validate_day(self, day, now=None):
        """
        Validate if <now> is in allowed <day> of week and of month.
        """
        n_col = day.count(":")
        day_of_month = None
        from_tail = None
        from_head = None
        if n_col > 1:
            raise SchedSyntaxError("only one ':' allowed in day spec '%s'" %day)
        elif n_col == 1:
            day, day_of_month = day.split(":")
            if len(day_of_month) == 0:
                raise SchedSyntaxError("day_of_month specifier is empty")
            if day_of_month in ("first", "1st"):
                from_head = True
                day_of_month = 1
            elif day_of_month in ("second", "2nd"):
                from_head = True
                day_of_month = 2
            elif day_of_month in ("third", "3rd"):
                from_head = True
                day_of_month = 3
            elif day_of_month in ("fourth", "4th"):
                from_head = True
                day_of_month = 4
            elif day_of_month in ("fifth", "5th"):
                from_head = True
                day_of_month = 5
            elif day_of_month == "last":
                from_tail = True
                day_of_month = 1
            elif day_of_month[0] == "-":
                from_tail = True
                day_of_month = day_of_month[1:]
            elif day_of_month[0] == "+":
                from_head = True
                day_of_month = day_of_month[1:]
            try:
                day_of_month = int(day_of_month)
            except ValueError:
                raise SchedSyntaxError("day_of_month is not a number")

        day = self._sched_expand_value(day)

        if day in ("*", ""):
            allowed_days = range(7)
        else:
            allowed_days = [d for d in day if d >= 0 and d <= 6]
        if now is None:
            now = datetime.datetime.now()
        this_week_day = now.weekday()

        if this_week_day not in allowed_days:
            raise SchedNotAllowed

        if day_of_month is not None:
            _day = now
            _month = _day.month
            if from_head is True:
                if day == "":
                    day1 = _day - datetime.timedelta(days=day_of_month)
                    day2 = _day - datetime.timedelta(days=day_of_month-1)
                else:
                    day1 = _day - datetime.timedelta(days=7*day_of_month)
                    day2 = _day - datetime.timedelta(days=7*(day_of_month-1))
                if day1.month == _month or day2.month != _month:
                    raise SchedNotAllowed
            elif from_tail is True:
                if day == "":
                    day1 = _day + datetime.timedelta(days=day_of_month)
                    day2 = _day + datetime.timedelta(days=day_of_month-1)
                else:
                    day1 = _day + datetime.timedelta(days=7*day_of_month)
                    day2 = _day + datetime.timedelta(days=7*(day_of_month-1))
                if day1.month == _month or day2.month != _month:
                    raise SchedNotAllowed
            elif _day.day != day_of_month:
                raise SchedNotAllowed

        return

    def _sched_validate_week(self, week, now=None):
        """
        Validate if <now> is in allowed <week>.
        """
        week = self._sched_expand_value(week)

        if week == "*":
            return

        allowed_weeks = [w for w in week if w >= 1 and w <= 53]
        if now is None:
            now = datetime.datetime.now()
        if now.isocalendar()[1] not in allowed_weeks:
            raise SchedNotAllowed("not in allowed weeks")
        return

    def _sched_validate_month(self, month, now=None):
        """
        Validate if <now> is in allowed <month>.
        """
        if month == "*":
            return

        allowed_months = set()
        for _month in month.split(","):
            ecount = _month.count("%")
            if ecount == 1:
                month_s, modulo_s = _month.split("%")
            elif ecount == 0:
                month_s = _month
                modulo_s = None
            else:
                raise SchedSyntaxError("only one '%%' allowed in month definition '%s'" % _month)


            if month_s in ("", "*"):
                _allowed_months = set(range(12))
            else:
                _allowed_months = self._sched_expand_value(month_s)
            if modulo_s is not None:
                _allowed_months &= self.__sched_validate_month(modulo_s)
            allowed_months |= _allowed_months

        if now is None:
            now = datetime.datetime.now()
        if now.month not in allowed_months:
            raise SchedNotAllowed("not in allowed months")
        return

    @staticmethod
    def __sched_validate_month(modulo):
        shift = 0
        n_plus = modulo.count("+")
        if n_plus > 1:
            raise SchedSyntaxError("only one '+' is allowed in modulo '%s'" % modulo)
        if n_plus == 1:
            modulo, shift = modulo.split("+")
        try:
            modulo = int(modulo)
        except ValueError:
            raise SchedSyntaxError("modulo '%s' is not a number" % modulo)
        try:
            shift = int(shift)
        except ValueError:
            raise SchedSyntaxError("shift '%s' is not a number" % shift)
        return set([m for m in range(1, 13) if (m + shift) % modulo == 0])

    @staticmethod
    def _sched_to_int(name):
        try:
            idx = int(name)
            return idx
        except ValueError:
            name = name.lower()
            if name not in CALENDAR_NAMES:
                raise SchedSyntaxError("unknown calendar name '%s'" % name)
            return CALENDAR_NAMES[name]

    def _sched_expand_value(self, spec):
        """
        Top level schedule definition parser.
        Split the definition into sub-schedules, and parse each one.
        """
        elements = set()
        if spec in ("*", ""):
            return spec
        subspecs = spec.split(",")
        for subspec in subspecs:
            n_dash = subspec.count("-")
            if n_dash > 1:
                raise SchedSyntaxError("only one '-' allowed in timerange '%s'" % spec)
            elif n_dash == 0:
                elements.add(self._sched_to_int(subspec))
                continue
            begin, end = subspec.split("-")
            begin = self._sched_to_int(begin)
            end = self._sched_to_int(end)
            _range = sorted([begin, end])
            elements |= set(range(_range[0], _range[1]+1))
        return elements

    def _interval_from_timerange(self, timerange):
        """
        Return a default interval from a timerange data structure.
        This interval is the timerange length in minute, plus one.
        """
        begin_m = self._time_to_minutes(timerange['begin'])
        end_m = self._time_to_minutes(timerange['end'])
        if begin_m > end_m:
            return 24 * 60 - begin_m + end_m + 1
        return end_m - begin_m + 1

    def _sched_parse_timerange(self, spec, section=None):
        """
        Return the list of timerange data structure parsed from the <spec>
        definition string.
        """

        def parse_timerange(spec):
            if spec == "*" or spec == "":
                return {"begin": "00:00", "end": "23:59"}
            if "-" not in spec:
                spec = "-".join((spec, spec))
            try:
                begin, end = spec.split("-")
            except:
                raise SchedSyntaxError("split '%s' error" % spec)
            if begin.count(":") != 1 or \
               end.count(":") != 1:
                raise SchedSyntaxError("only one ':' allowed in timerange '%s' end" % spec)
            begin_m = self._time_to_minutes(begin)
            end_m = self._time_to_minutes(end)
            if begin_m == end_m:
                end_m += 10
                end = "%02d:%02d" % (end_m // 60, end_m % 60)
            return {"begin": begin, "end": end}

        if section and section.startswith("sync"):
            probabilistic = False
        else:
            probabilistic = True

        tr_list = []
        for _spec in spec.split(","):
            if len(_spec) == 0 or _spec == "*":
                tr_data = {
                    "probabilistic": probabilistic,
                    "begin": "00:00",
                    "end": "23:59",
                    "interval": 1441,
                }
                tr_list.append(tr_data)
                continue
            ecount = _spec.count("@")
            if ecount == 0:
                tr_data = parse_timerange(_spec)
                tr_data["interval"] = self._interval_from_timerange(tr_data)
                tr_data["probabilistic"] = probabilistic
                tr_list.append(tr_data)
                continue

            elements = _spec.split("@")
            ecount = len(elements)
            if ecount < 2:
                raise SchedSyntaxError("missing @<interval> in '%s'" % _spec)
            if ecount > 2:
                raise SchedSyntaxError("only one @<interval> allowed in '%s'" % _spec)
            tr_data = parse_timerange(elements[0])
            tr_data["probabilistic"] = probabilistic
            try:
                tr_data["interval"] = convert_duration(elements[1], _from="m", _to="m")
            except ValueError as exc:
                raise SchedSyntaxError("interval '%s' is not a valid duration expression: %s" % (elements[1], exc))
            tr_list.append(tr_data)
        return tr_list

    def sched_get_schedule(self, section, option, schedules=None):
        """
        Return the list of schedule structures for the spec string passed
        as <schedules> or, if not passed, from the <section>.<option> value
        in the configuration file.
        """
        if schedules is None:
            schedules = self.sched_get_schedule_raw(section, option)
        try:
            schedules = json.loads(schedules)
        except:
            pass
        if is_string(schedules):
            schedules = [schedules]

        data = []
        for schedule in schedules:
            schedule_orig = schedule
            schedule = schedule.strip()
            if len(schedule) == 0:
                continue
            if schedule.startswith("!"):
                exclude = True
                schedule = schedule[1:].strip()
            else:
                exclude = False
            if len(schedule) == 0:
                continue
            elements = schedule.split()
            ecount = len(elements)
            if ecount == 1:
                _data = {
                    "timeranges": self._sched_parse_timerange(elements[0], section=section),
                    "day": "*",
                    "week": "*",
                    "month": "*",
                }
            elif ecount == 2:
                _tr, _day = elements
                _data = {
                    "timeranges": self._sched_parse_timerange(_tr, section=section),
                    "day": _day,
                    "week": "*",
                    "month": "*",
                }
            elif ecount == 3:
                _tr, _day, _week = elements
                _data = {
                    "timeranges": self._sched_parse_timerange(_tr, section=section),
                    "day": _day,
                    "week": _week,
                    "month": "*",
                }
            elif ecount == 4:
                _tr, _day, _week, _month = elements
                _data = {
                    "timeranges": self._sched_parse_timerange(_tr, section=section),
                    "day": _day,
                    "week": _week,
                    "month": _month,
                }
            else:
                raise SchedSyntaxError("invalid number of element, '%d' not in "
                                       "(1, 2, 3, 4)" % ecount)
            _data["exclude"] = exclude
            _data["raw"] = schedule_orig
            data.append(_data)
        return data

    def allow_action_schedule(self, section, option, fname=None, now=None, last=None):
        if option is None:
            return
        if now is None:
            now = datetime.datetime.now()
        try:
            schedule = self.sched_get_schedule(section, option)
            delay = self.in_schedule(schedule, fname=fname, now=now, last=last)
            return delay
        except SchedNoDefault:
            raise SchedNotAllowed("no schedule in section %s and no default "
                                  "schedule"%section)
        except SchedSyntaxError as exc:
            raise SchedNotAllowed("malformed parameter value: %s.schedule "
                                  "(%s)"%(section, str(exc)))

    def skip_action_schedule(self, section, option, fname=None, now=None, last=None):
        try:
            self.allow_action_schedule(section, option, fname=fname, now=now, last=last)
            return False
        except SchedExcluded:
            return True
        except Exception:
            return True
        return True

    def get_timestamp_f(self, fname, success=False):
        """
        Return the full path of the last run timestamp file with the <fname>
        basename.
        """
        if self.name == "node":
            timestamp_d = os.path.join(rcEnv.paths.pathvar, "node", "scheduler")
        else:
            timestamp_d = os.path.join(rcEnv.paths.pathvar, "services", self.name, "scheduler")
        if not os.path.exists(timestamp_d):
            os.makedirs(timestamp_d)
        fpath = os.path.join(timestamp_d, fname)
        if success:
            fpath += ".success"
        return fpath

    def validate_action(self, action):
        """
        Decide if the scheduler task can run, and return the concerned rids
        for multi-resources actions.

        The callers are responsible for catching excAbortAction.
        """
        if not self._is_croned():
            return
        if action not in self.scheduler_actions:
            return
        if not isinstance(self.scheduler_actions[action], list):
            skip = self.skip_action(action)
            if skip is True:
                raise ex.excAbortAction
            return skip
        data = self.skip_action(action)
        sched_options = data["keep"]
        if len(sched_options) == 0:
            raise ex.excAbortAction
        return [option.section for option in sched_options], data["delay"]

    def action_timestamps(self, action, rids=None, success=False):
        sched_options = self.scheduler_actions[action]
        tsfiles = []
        if not isinstance(sched_options, list):
            tsfile = self.get_timestamp_f(sched_options.fname, success=success)
            tsfiles.append(tsfile)
        else:
            if rids is None:
                return []
            for _so in sched_options:
                if not _so.section in rids:
                    continue
                tsfile = self.get_timestamp_f(_so.fname, success=success)
                tsfiles.append(tsfile)

        if len(tsfiles) == 0:
            return []

        fds = []
        import lock
        try:
            for tsfile in tsfiles:
                if not success:
                    fd = lock.lock(timeout=0, lockfile=tsfile+".lock")
                    fds.append(fd)
                self._timestamp(tsfile)
        except lock.LOCK_EXCEPTIONS:
            self.log.warning("a %s action is already in progess", action)
            for fd in fds:
                lock.unlock(fd)
            raise
        except Exception as exc:
            for fd in fds:
                lock.unlock(fd)
            raise ex.excError("error updating timestamp %s: %s"%(tsfile, exc))
        return fds

    def _is_croned(self):
        """
        Return True if the cron option is set.
        """
        return self.options.cron

    def skip_action(self, action, section=None, fname=None,
                    schedule_option=None, now=None):
        if action not in self.scheduler_actions:
            if not self._is_croned():
                return False
            return {"count": 0, "keep": [], "skip": []}

        if isinstance(self.scheduler_actions[action], list):
            data = {"count": 0, "keep": [], "skip": []}
            idx = 0
            for idx, sopt in enumerate(self.scheduler_actions[action]):
                skip = self._skip_action(
                    action, sopt,
                    section=section, fname=fname, schedule_option=schedule_option,
                    now=now
                )
                if skip is True:
                    data["skip"].append(sopt)
                else:
                    data["keep"].append(sopt)
                    if "delay" not in data or skip < data["delay"]:
                        data["delay"] = skip
            data["count"] = idx + 1
            return data

        else:
            sopt = self.scheduler_actions[action]
            return self._skip_action(
                action, sopt,
                section=section, fname=fname, schedule_option=schedule_option,
                now=now
            )

    def _skip_action(self, action, sopt, section=None, fname=None,
                     schedule_option=None, now=None):
        if section is None:
            section = sopt.section
        if fname is None:
            fname = sopt.fname
        if schedule_option is None:
            schedule_option = sopt.schedule_option

        def title():
            """
            Return a string to use as the task title in log entries.
            """
            buff = ".".join((self.name, action))
            if "#" in section:
                buff += "." + section
            return buff

        if not self._is_croned():
            # don't update the timestamp file
            return False

        # check if we are in allowed scheduling period
        try:
            delay = self.allow_action_schedule(section, schedule_option, fname=fname, now=now)
        except Exception as exc:
            self.sched_log(title(), str(exc), "debug")
            return True

        #self.sched_log(title(), "run task", "info")
        return delay

    def print_schedule(self):
        """
        The 'print schedule' node and service action entrypoint.
        """
        if not hasattr(self, "config") or self.config is None:
            print("you are not allowed to print schedules", file=sys.stderr)
            raise ex.excError()
        if self.options.format is None:
            self._print_schedule_default()
            return
        data = self._print_schedule_data()
        if self.svc and len(self.svc.node.svcs) > 1:
            # let the Node object do the formatting (for aggregation)
            return data
        # format ourself
        return self._print_schedule(data)

    @formatter
    def _print_schedule(self, data):
        """
        Display the scheduling table using the formatter specified in
        command line --format option.
        """
        return data

    def _print_schedule_default(self):
        """
        Print the scheduling table in normal or detailed mode.
        """
        from forest import Forest
        from rcColor import color
        tree = Forest()
        head_node = tree.add_node()
        head_node.add_column("Action", color.BOLD)
        head_node.add_column("Last Run", color.BOLD)
        if self.options.verbose:
            head_node.add_column("Next Run", color.BOLD)
        head_node.add_column("Config Parameter", color.BOLD)
        head_node.add_column("Schedule Definition", color.BOLD)

        for data in self._print_schedule_data():
            node = head_node.add_node()
            node.add_column(data["action"], color.LIGHTBLUE)
            node.add_column(data["last_run"])
            if self.options.verbose:
                node.add_column(data["next_run"])
            node.add_column(data["config_parameter"])
            node.add_column(data["schedule_definition"])

        tree.print()

    def _print_schedule_data(self):
        """
        Return a list of dict of schedule information for all tasks.
        """
        data = []
        for action in sorted(self.scheduler_actions):
            data += self.__print_schedule_data(action)
        return data

    def __print_schedule_data(self, action):
        """
        Return a dict of a scheduled task, or list of dict of a task-set,
        containing schedule information.
        """
        data = []
        if isinstance(self.scheduler_actions[action], list):
            for sopt in self.scheduler_actions[action]:
                data += [self.___print_schedule_data(action, sopt)]
        else:
            sopt = self.scheduler_actions[action]
            data += [self.___print_schedule_data(action, sopt)]
        return data

    def ___print_schedule_data(self, action, sopt):
        """
        Return a dict of a scheduled task information.
        """
        section = sopt.section
        schedule_option = sopt.schedule_option
        fname = sopt.fname

        try:
            schedule_s = self.sched_get_schedule_raw(section, schedule_option)
        except SchedNoDefault:
            schedule_s = "anytime"
        except SchedSyntaxError:
            schedule_s = "malformed"
        if len(schedule_s) == 0:
            schedule_s = "-"

        timestamp_f = self.get_timestamp_f(fname)
        try:
            with open(timestamp_f, 'r') as ofile:
                last_s = ofile.read()
                last_s = last_s.split('.')[0]
        except (IOError, OSError):
            last_s = "-"

        if section != "DEFAULT":
            param = "schedule"
        else:
            param = schedule_option
        param = '.'.join((section, param))
        if self.options.verbose:
            result = self.get_next_schedule(action)
            if result["next_sched"]:
                next_s = result["next_sched"].strftime("%Y-%m-%d %H:%M")
            else:
                next_s = "-"
            return dict(
                action=action,
                last_run=last_s,
                next_run=next_s,
                config_parameter=param,
                schedule_definition=schedule_s
            )
        else:
            return dict(
                action=action,
                last_run=last_s,
                config_parameter=param,
                schedule_definition=schedule_s,
            )

    @staticmethod
    def _str_to_datetime(datetime_str):
        """
        Convert a %Y-%m-%d %H:%M formatted string to a datetime.
        """
        converted = datetime.datetime.strptime(datetime_str, "%Y-%m-%d %H:%M")
        return converted

    def test_schedule(self, schedule_s, date_s, expected, last_s):
        """
        Test if <date_s> passes <schedule_s> constraints and compares with the
        expected boolean result <expected>.
        Print a test report line.
        This method is used by the test_scheduler() testing function.
        """
        dtm = self._str_to_datetime(date_s)
        if last_s:
            last = self._str_to_datetime(last_s)
        else:
            last = None

        try:
            schedule = self.sched_get_schedule("dummy", "dummy", schedules=schedule_s)
        except SchedSyntaxError as exc:
            if expected == None:
                print("passed : schedule syntax error %s (%s)" % (repr(schedule_s), str(exc)))
                return True
            else:
                print("failed : schedule syntax error %s (%s)" % (repr(schedule_s), str(exc)))
                return False
        try:
            delay = self.in_schedule(schedule, fname=None, now=dtm, last=last)
            result = True
            result_s = ""
        except SchedSyntaxError as exc:
            if expected == None:
                print("passed : schedule syntax error %s (%s)" % (repr(schedule_s), str(exc)))
                return True
            else:
                print("failed : schedule syntax error %s (%s)" % (repr(schedule_s), str(exc)))
                return False
        except SchedNotAllowed as exc:
            result = False
            result_s = "("+str(exc)+")"

        if result == expected:
            check = "passed"
            ret = True
        else:
            check = "failed"
            ret = False

        print("%s : now '%s' last %-18s in schedule %-50s expected %s => result %s %s" % \
              (check, date_s, repr(last_s), repr(schedule_s), str(expected), str(result), result_s))

        return ret

def test_scheduler():
    """
    A exercizing function for the scheduler.
    """
    tests = [
        ("", "2015-02-27 10:00", False),
        ("@0", "2015-02-27 10:00", False),
        ("*@0", "2015-02-27 10:00", False),
        ("*", "2015-02-27 10:00", True),
        ("*@61", "2015-02-27 10:00", True),
        ("09:00-09:20", "2015-02-27 10:00", False),
        ("09:00-09:20@31", "2015-02-27 10:00", False),
        ("09:00-09:00", "2015-02-27 10:00", False),
        ("09:20-09:00", "2015-02-27 10:00", False),
        ("09:00", "2015-02-27 10:00", False),
        ("09:00-09:20", "2015-02-27 09:09", True),
        ("09:00-09:20@31", "2015-02-27 09:09", True),
        ("09:00-09:00", "2015-02-27 09:09", True),
        ("09:20-09:00", "2015-02-27 09:09", True),
        ("09:00", "2015-02-27 09:09", True),
        ("* fri", "2015-10-09 10:00", True),
        ("* fri", "2015-10-08 10:00", False),
        ("* *:last", "2015-01-30 10:00", True),
        ("* *:last", "2015-01-31 10:00", True),
        ("* *:-1", "2015-01-31 10:00", True),
        ("* *:-1", "2015-01-24 10:00", False),
        ("* *:-2", "2015-01-31 10:00", False),
        ("* :last", "2015-01-30 10:00", False),
        ("* :last", "2015-01-31 10:00", True),
        ("* :-1", "2015-01-31 10:00", True),
        ("* :-2", "2015-01-30 10:00", True),
        ("* :-2", "2015-01-31 10:00", False),
        ("* :-2", "2015-01-05 10:00", False),
        ("* :5", "2015-01-05 10:00", True),
        ("* :+5", "2015-01-05 10:00", True),
        ("* :fifth", "2015-01-05 10:00", True),
        ("* :5", "2015-01-06 10:00", False),
        ("* :+5", "2015-01-06 10:00", False),
        ("* :fifth", "2015-01-06 10:00", False),
        ("* * * jan", "2015-01-06 10:00", True),
        ("* * * jan-feb", "2015-01-06 10:00", True),
        ("* * * %2", "2015-01-06 10:00", False),
        ("* * * %2+1", "2015-01-06 10:00", True),
        ("* * * jan-feb%2", "2015-01-06 10:00", False),
        ("* * * jan-feb%2+1", "2015-01-06 10:00", True),
        ("18:00-18:59@60 wed", "2016-08-31 18:00", True),
        ("18:00-18:59@60 wed", "2016-08-30 18:00", False),
        ("23:00-23:59@61 *:first", "2016-09-01 23:00", True),
        # syntax errors
        ("23:00-23:59@61 *:first:*", "2016-09-01 23:00", None),
        ("23:00-23:59@61 *:", "2016-09-01 23:00", None),
        ("23:00-23:59@61 *:*", "2016-09-01 23:00", None),
        ("23:00-23:59@61 * * %2%3", "2016-09-01 23:00", None),
        ("23:00-23:59@61 * * %2+1+2", "2016-09-01 23:00", None),
        ("23:00-23:59@61 * * %foo", "2016-09-01 23:00", None),
        ("23:00-23:59@61 * * %2+foo", "2016-09-01 23:00", None),
        ("23:00-23:59@61 freday", "2016-09-01 23:00", None),
        ("23:00-23:59@61 * * junuary", "2016-09-01 23:00", None),
        ("23:00-23:59@61 * * %2%3", "2016-09-01 23:00", None),
        ("23:00-23:59-01:00@61", "2016-09-01 23:00", None),
        ("23:00-23:59:00@61 * * %2%3", "2016-09-01 23:00", None),
        ("23:00-23:59@61@10", "2016-09-01 23:00", None),
        ("23:00-23:59 * * * * *", "2016-09-01 23:00", None),
    ]
    sched = Scheduler()
    for test in tests:
        assert sched.test_schedule(*test)

if __name__ == "__main__":
    test_scheduler()
   0707010005c972000081b40000000000000000000000015ab2175400000ff6000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/rcStatsAIX.py  import os
import datetime
from rcUtilities import call, which
from rcGlobalEnv import rcEnv

today = datetime.datetime.today()
yesterday = today - datetime.timedelta(days=1)

def sarfile(day):
    f = os.path.join(os.sep, 'var', 'adm', 'sa', 'sa'+day)
    if os.path.exists(f):
        return f
    return None

def twodays(fn):
    if which('sar') is None:
        return []
    lines = fn(yesterday)
    lines += fn(today)
    return lines

def stats_cpu():
    return twodays(stats_cpu_day)

def stats_cpu_day(t):
    d = t.strftime("%Y-%m-%d")
    day = t.strftime("%d")
    f = sarfile(day)
    if f is None:
        return []
    cmd = ['sar', '-u', '-P', 'ALL', '-f', f]
    (ret, buff, err) = call(cmd, errlog=False)
    lines = []
    for line in buff.split('\n'):
        l = line.split()
        if len(l) != 6:
            continue
        if l[1] == '%usr':
            continue
        if l[0] == 'Average':
            continue
        # SunOS:  date    %usr     %sys %wio                       %idle
        # xmlrpc: date cpu usr nice sys iowait steal irq soft guest idle nodename
        x = ['%s %s'%(d, l[0]), 'all', '0', '0', '0', '0', '0', '0', '0', '0', '0', rcEnv.nodename]
        x[1] = l[1].replace('-', 'all')
        x[2] = l[2]
        x[4] = l[3]
        x[5] = l[4]
        x[10] = l[5]
        lines.append(x)
    return lines

def stats_mem_u(file, collect_date=None):
    return twodays(stats_mem_u_day)

def stats_mem_u_day(t):
    return []

def stats_proc(file, collect_date=None):
    return twodays(stats_proc_day)

def stats_proc_day(t):
    d = t.strftime("%Y-%m-%d")
    day = t.strftime("%d")
    f = sarfile(day)
    if f is None:
        return []
    cmd = ['sar', '-q', '-f', f]
    (ret, buff, err) = call(cmd)
    lines = []
    for line in buff.split('\n'):
        l = line.split()
        if len(l) < 3:
            continue
        if ':' not in l[0]:
            continue
        """ xmlrpc: date runq_sz plist_sz ldavg_1 ldavg_5 ldavg_15 nodename
        """
        x = ['%s %s'%(d, l[0]), l[1], '0', '0', '0', '0', rcEnv.nodename]
        lines.append(x)
    return lines

def stats_swap(file, collect_date=None):
    return twodays(stats_swap_day)

def stats_swap_day(t):
    return []

def stats_block(file, collect_date=None):
    return twodays(stats_block_day)

def stats_block_day(t):
    d = t.strftime("%Y-%m-%d")
    day = t.strftime("%d")
    f = sarfile(day)
    if f is None:
        return []
    cmd = ['sar', '-b', '-f', f]
    (ret, buff, err) = call(cmd)
    lines = []
    for line in buff.split('\n'):
        l = line.split()
        if len(l) != 9:
            continue
        if ':' not in l[1]:
            continue

        """ xmlrpc: date tps rtps wtps rbps wbps nodename
        """
        x = ['%s %s'%(d, l[0]), '0', '0', '0', l[1], l[4], rcEnv.nodename]

        lines.append(x)
    return lines

def stats_blockdev(file, collect_date=None):
    return twodays(stats_blockdev_day)

def stats_blockdev_day(t):
    d = t.strftime("%Y-%m-%d")
    day = t.strftime("%d")
    f = sarfile(day)
    if f is None:
        return []
    cmd = ['sar', '-d', '-f', f]
    (ret, buff, err) = call(cmd, errlog=False)
    lines = []
    for line in buff.split('\n'):
        l = line.split()
        if len(l) == 8:
            date = l[0]
        if len(l) == 7:
            l = [date] + l
        if len(l) != 8:
            continue
        if l[1] == 'device':
            continue
        if l[0] == 'Average':
            continue
        """ xmlrpc: 22:05:01 DEV tps rd_sec/s wr_sec/s avgrq-sz avgqu-sz await svctm %util
                    00:00:00 device %busy avque r+w/s blks/s avwait avserv
        """
        x = ['%s %s'%(d, l[0]), l[1], l[4], '0', '0', '0', l[3], l[6], l[7], l[2], rcEnv.nodename]
        lines.append(x)
    return lines

def stats_netdev(file, collect_date=None):
    return twodays(stats_netdev_day)

def stats_netdev_day(t):
    return []

def stats_netdev_err(file, collect_date=None):
    return twodays(stats_netdev_err_day)

def stats_netdev_err_day(t):
    return []

  0707010005c973000081b40000000000000000000000015ab2175400000aad000000660000000000000000000000000000003300000000root/usr/share/opensvc/lib/rcStatsCollectDarwin.py    import os
import sys
import datetime
from rcUtilities import justcall
from rcGlobalEnv import rcEnv

def collect(node):
    now = datetime.datetime.now()
    
    def fs_u():
        vars = ['date',
                'nodename',
                'mntpt',
                'size',
                'used']
    
        cmd = ['df', '-lkP']
        (out, err, ret) = justcall(cmd)
        if ret != 0:
            return self.undef
        lines = out.split('\n')
        if len(lines) < 2:
            return self.undef
        vals = []
        for line in lines[1:]:
            l = line.split()
            if len(l) != 6:
                continue
            if l[5].startswith('/Volumes'):
                # Darwin automount package files under /Volumes
                continue
            vals.append([str(now), node.nodename, l[5], l[1], l[4].replace('%','')])
        return (vars, vals)
    
    def mem_u():
        basedir = os.path.join(rcEnv.paths.pathvar, 'stats')
        if not os.path.exists(basedir):
            os.makedirs(basedir)
        fname = os.path.join(basedir, 'mem_u%0.2d'%now.day)
        if not os.path.exists(fname):
            try:
                f = open(fname, 'w')
            except:
                return
        else:
            mtime = os.stat(fname).st_mtime
            if datetime.datetime.fromtimestamp(mtime) < now - datetime.timedelta(days=1):
                os.unlink(fname)
                try:
                    f = open(fname, 'w')
                except:
                    return
            else:
                try:
                    f = open(fname, 'a')
                except:
                    return
     
    
        cmd = ['/usr/sbin/sysctl', '-n', 'hw.pagesize']
        (out, err, ret) = justcall(cmd)
        if ret != 0:
            return
        pagesize = int(out.split()[0])
    
        cmd = ['vm_stat']
        (out, err, ret) = justcall(cmd)
        if ret != 0:
            return
        h = {}
        for line in out.split('\n'):
            l = line.split(':')
            if len(l) != 2:
                continue
            key = l[0]
            try:
                val = int(l[1].strip(' .'))
            except:
                continue
            h[key] = val
        f.write(' '.join((now.strftime('%H:%M:%S'),
                          str(h['Pages free']*pagesize/1024),
                          str(h['Pages active']*pagesize/1024),
                          str(h['Pages inactive']*pagesize/1024),
                          str(h['Pages speculative']*pagesize/1024),
                          str(h['Pages wired down']*pagesize/1024)
                ))+'\n')
    
    
    node.collector.call('push_stats_fs_u', fs_u())
    mem_u()
   0707010005c974000081b40000000000000000000000015ab2175400000dec000000660000000000000000000000000000003200000000root/usr/share/opensvc/lib/rcStatsCollectHP-UX.py import os
import sys
import datetime
from subprocess import *
from rcUtilities import justcall, is_exe
from rcGlobalEnv import rcEnv

def collect(node):
    now = str(datetime.datetime.now())
    
    def fs_u():
        vars = ['date',
                'nodename',
                'mntpt',
                'size',
                'used']
    
        cmd = ['df', '-lP']
        (out, err, ret) = justcall(cmd)
        if ret != 0:
            return
        lines = out.split('\n')
        if len(lines) < 2:
            return
        vals = []
        for line in lines[1:]:
            l = line.split()
            if len(l) != 6:
                continue
            vals.append([now, node.nodename, l[5], l[1], l[4].replace('%','')])
        return (vars, vals)
    
    def glance_running(cmd_str):
        (out, err, ret) = justcall(['ps', '-ef'])
        if ret != 0:
            print('ps error')
            return
    
        for line in out.split('\n'):
            l = line.split()
            if len(l) < 6:
                continue
            if cmd_str in ' '.join(l[6:]):
                return True
    
        return False
    
    def run_glance():
        glance = '/opt/perf/bin/glance'
        syn = os.path.join(rcEnv.paths.pathtmp, 'glance.syntax')
        now = datetime.datetime.now()
        iterations = (23-now.hour)*6+(60-now.minute)//10
        cmd = ['/opt/perf/bin/glance', '-aos', syn, '-j', '600', '-iterations']
        cmd_str = ' '.join(cmd)
    
        if not is_exe(glance):
            print('glance executable not found')
            return
    
        if glance_running(cmd_str):
            print('glance is already running')
            return
    
        buff = """print GBL_STATTIME," ",
    // usr
    0.00+GBL_CPU_NORMAL_UTIL+GBL_CPU_REALTIME_UTIL," ",
    // nice
    0.00+GBL_CPU_NICE_UTIL+GBL_CPU_NNICE_UTIL," ",
    // sys
    0.00+GBL_CPU_SYSCALL_UTIL+GBL_CPU_CSWITCH_UTIL+GBL_CPU_TRAP_UTIL+GBL_CPU_VFAULT_UTIL," ",
    // irq
    0.00+GBL_CPU_INTERRUPT_UTIL," ",
    // wait
    0.00+GBL_CPU_WAIT_UTIL," ",
    // idle
    0.00+GBL_CPU_IDLE_UTIL-GBL_CPU_WAIT_UTIL," ",
    
    // mem
    0+GBL_MEM_PHYS," ",
    0+GBL_MEM_FREE," ",
    0+GBL_MEM_CACHE," ",
    0+GBL_MEM_FILE_PAGE_CACHE," ",
    0+GBL_MEM_SYS," ",
    0+GBL_MEM_USER," ",
    
    // swap
    0+GBL_MEM_SWAP," ",
    0+GBL_SWAP_SPACE_AVAIL-GBL_MEM_PHYS," ",
    
    // load
    GBL_LOADAVG," ",
    GBL_LOADAVG5," ",
    GBL_LOADAVG15," ",
    GBL_CPU_QUEUE," ",
    
    // process list
    TBL_PROC_TABLE_USED," ",
    
    // disk io
    GBL_DISK_PHYS_READ_RATE," ",
    GBL_DISK_PHYS_WRITE_RATE," ",
    
    // disk kB/s
    GBL_DISK_PHYS_READ_BYTE_RATE," ",
    GBL_DISK_PHYS_WRITE_BYTE_RATE
    """
        try:
            with open(syn, 'w') as f:
                f.write(buff)
        except:
            print('error writing %s'%syn)
            return
    
        collect_d = os.path.join(rcEnv.paths.pathvar, "stats")
        collect_f = 'glance%0.2d'%now.day
        collect_p = os.path.join(collect_d, collect_f)
    
        if os.path.exists(collect_p):
            mtime = os.stat(collect_p).st_mtime
            if datetime.datetime.fromtimestamp(mtime) < now - datetime.timedelta(days=1):
                os.unlink(collect_p)
    
        _cmd = 'nohup %s %d >>%s &'%(cmd_str, iterations, collect_p)
        process = Popen(_cmd, shell=True, stdout=PIPE, stderr=PIPE)
    
    
    run_glance()
    fs_u_data = fs_u()
    if fs_u_data is not None:
        node.collector.call('push_stats_fs_u', fs_u_data)
    
0707010005c975000081b40000000000000000000000015ab2175400001410000000660000000000000000000000000000003200000000root/usr/share/opensvc/lib/rcStatsCollectLinux.py from __future__ import print_function
import os
import sys
import datetime
import time
import json
import re
from rcUtilities import justcall, which
from rcGlobalEnv import rcEnv

mntpt_blacklist = [
    "/proc",
    "/sys/fs/cgroup",
    "/run/user/[0-9]+",
    "(/var){0,1}/run/user/[0-9]+",
    ".*/docker/.*/[0-9a-f]{64}.*",
]

def collect(node):
    now = str(datetime.datetime.now())

    def blacklisted(mntpt):
        for bl in mntpt_blacklist:
            if re.match(bl, mntpt):
                return True

    def fs_u():
        cmd = ['df', '-lP']
        (out, err, ret) = justcall(cmd)
        if ret != 0:
            return
        lines = out.split('\n')
        if len(lines) < 2:
            return
        vals = []
        for line in lines[1:]:
            l = line.split()
            if len(l) != 6:
                continue
            if blacklisted(l[5]):
                continue
            vals.append([now, node.nodename, l[5], l[1], l[4].replace('%','')])

        stats_fs_u_d = os.path.join(rcEnv.paths.pathvar, "stats")
        stats_fs_u_p = os.path.join(stats_fs_u_d, 'fs_u.%d' % datetime.datetime.now().day)

        if not os.path.exists(stats_fs_u_d):
            os.makedirs(stats_fs_u_d)
        if not os.path.exists(stats_fs_u_p):
            # create the stats file
            mode = 'w+'
        elif os.stat(stats_fs_u_p).st_mtime < time.time() - 86400:
            # reset the stats file from last month
            mode = 'w+'
        else:
            # append to the daily stats file
            mode = 'a'
    
        with open(stats_fs_u_p, mode) as f:
            f.write(json.dumps(vals)+'\n')
    
    """
    xentop
    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
    """
    
    def xentop(node):
        import os, sys, platform
        import select
        import logging
        import time
        import datetime
        import subprocess
    
        if not which('xentop'):
            return
    
        node.build_services()
        containernames = {}
        for svc in node.svcs:
            for r in svc.get_resources("container"):
                if r.type in ("container.ovm", "container.xen"):
                    if hasattr(r, "uuid"):
                        containernames[r.uuid] = r.name
    
        zs_d = os.path.join(rcEnv.paths.pathlog, 'xentop')
        zs_prefix = 'xentop'
        zs_f = os.path.join(zs_d, zs_prefix + datetime.datetime.now().strftime("%d"))
        datenow = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        n = datetime.datetime.now()
        tn = time.mktime(n.timetuple())
        
        if not os.path.exists(zs_d):
            os.makedirs(zs_d)
        
        try:
            t = os.path.getmtime(zs_f)
            d = tn - t
        except:
            d = 0
        
        if d > 27*24*3600:
            os.remove(zs_f)
        
        f = open(zs_f, "a")
        
        stor = {}
        
        p = subprocess.Popen('xentop -b -d.1 -i2 -f',
                             stdout=subprocess.PIPE,
                             stderr=subprocess.STDOUT,
                             shell=True,
                             bufsize=0)
        
        out = p.stdout.readline()
        pr = 0
        
        while out:
            line = out
            line = line.rstrip("\n")
        
            if "NAME" in line:
                pr += 1
                out = p.stdout.readline()
                continue
        
            line = line.replace("no limit", "0")
            fields = line.split()
            if len(fields) == 19 and pr > 1:
                uuid = fields[0]
                if uuid in containernames:
                    uuid = containernames[uuid]
                stor[uuid] = {
                 'STATE': fields[1],
                 'CPU_SEC': fields[2],
                 'CPU_PCT': fields[3],
                 'MEM': str(int(fields[4])//1024),
                 'MEM_PCT': fields[5],
                 'MEM_MAX': str(int(fields[6])//1024),
                 'MEM_MAX_PCT': fields[7],
                 'VCPUS': fields[8],
                 'NETS': fields[9],
                 'NET_TX': fields[10],
                 'NET_RX': fields[11],
                 'VBDS': fields[12],
                 'VBD_OO': fields[13],
                 'VBD_RD': fields[14],
                 'VBD_WR': fields[15],
                 'VBD_RSECT': fields[16],
                 'VBD_WSECT': fields[17],
                 'SSID': fields[18] 
                }
                print(datenow, uuid, stor[uuid]['STATE'], stor[uuid]['CPU_SEC'], stor[uuid]['CPU_PCT'], stor[uuid]['MEM'], stor[uuid]['MEM_PCT'], stor[uuid]['MEM_MAX'], stor[uuid]['MEM_MAX_PCT'], stor[uuid]['VCPUS'], stor[uuid]['NETS'], stor[uuid]['NET_TX'], stor[uuid]['NET_RX'], stor[uuid]['VBDS'], stor[uuid]['VBD_OO'], stor[uuid]['VBD_RD'], stor[uuid]['VBD_WR'], stor[uuid]['VBD_RSECT'], stor[uuid]['VBD_WSECT'], stor[uuid]['SSID'], file=f)
            out = p.stdout.readline()
        
        p.wait()
    
    fs_u()
    xentop(node)
    
0707010005c976000081b40000000000000000000000015ab21754000017ca000000660000000000000000000000000000003200000000root/usr/share/opensvc/lib/rcStatsCollectSunOS.py """
YYYY-MM-DD hh:mm:ss ZONE SWAP RSS CAP at avgat pg avgpg NPROC mem% cpu% TIME LastReboot
"""

from __future__ import print_function
import os, sys, platform
import time
import datetime
import subprocess
from rcUtilities import justcall, which
from rcGlobalEnv import rcEnv

def collect(node):
    now = str(datetime.datetime.now())

    zs_d = os.path.join(os.sep, 'var', 'adm', 'zonestat')
    zs_prefix = 'zs'
    zs_f = os.path.join(zs_d, zs_prefix + datetime.datetime.now().strftime("%d"))
    datenow = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    n = datetime.datetime.now()
    tn = time.mktime(n.timetuple())

    if not os.path.exists(zs_d):
        os.makedirs(zs_d)

    try:
        t = os.path.getmtime(zs_f)
        d = tn - t
    except:
        d = 0

    if d > 27*24*3600:
        os.remove(zs_f)

    f = open(zs_f, "a")

    stor = {}

    p = subprocess.Popen('/usr/bin/prstat -Z -n1,60 1 1',
                         stdout=subprocess.PIPE,
                         stderr=subprocess.STDOUT,
                         shell=True,
                         bufsize=0)

    out = p.stdout.readline()
    pr = 0

    while out:
        line = out
        line = line.rstrip("\n")

        if "ZONEID" in line:
            pr = 1
            out = p.stdout.readline()
            continue

        if "Total:" in line:
            pr = 0
            out = p.stdout.readline()
            continue

        if "%" in line and pr == 1:
            fields = line.split()
            stor[fields[7]] = {
             'SWAP': fields[2],
             'RSS': fields[3],
             'CAP': '0',
             'at': '0',
             'avgat': '0',
             'pg': '0',
             'avgpg': '0',
             'NPROC': fields[1],
             'mem': fields[4],
             'cpu': fields[6],
             'TIME': fields[5]
            }
        out = p.stdout.readline()

    p.wait()
    fi=1
    pr=0
    p = subprocess.Popen('/usr/bin/rcapstat -z 1 2',
                         stdout=subprocess.PIPE,
                         stderr=subprocess.STDOUT,
                         bufsize=0,
                         shell=True)
    out = p.stdout.readline()

    while out:
        line = out
        line = line.rstrip("\n")

        if "id zone" in line and fi == 1:
            fi = 0
            out = p.stdout.readline()
            continue

        if "id zone" in line and fi == 0:
            pr = 1
            out = p.stdout.readline()
            continue

        if not "id zone" in line and pr == 1:
            fields = line.split()
            stor[fields[1]]['CAP'] = fields[5]
            stor[fields[1]]['at'] = fields[6]
            stor[fields[1]]['avgat'] = fields[7]
            stor[fields[1]]['pg'] = fields[8]
            stor[fields[1]]['avgpg'] = fields[9]

        out = p.stdout.readline()

    p.wait()

    for z in stor:
        zn = z
        if z == 'global' :
            zn = platform.node()
            p = subprocess.Popen('/usr/bin/who -b',
                             stdout=subprocess.PIPE,
                             stderr=subprocess.STDOUT,
                             bufsize=0,
                             shell=True)
        else :
            p = subprocess.Popen('/usr/sbin/zlogin ' + z + ' /usr/bin/who -b',
                             stdout=subprocess.PIPE,
                             stderr=subprocess.STDOUT,
                             bufsize=0,
                             shell=True)
        out = p.stdout.readline()
        txt = out.split()
        print(datenow, zn, stor[z]['SWAP'], stor[z]['RSS'], stor[z]['CAP'], stor[z]['at'], stor[z]['avgat'], stor[z]['pg'], stor[z]['avgpg'], stor[z]['NPROC'], stor[z]['mem'], stor[z]['cpu'], stor[z]['TIME'], txt[-3], txt[-2], txt[-1], file=f)
        p.wait()


    """
     fs_u
    """
    def fs_u():
        vars = ['date',
                'nodename',
                'mntpt',
                'size',
                'used']
        vals = []
        vals += fs_u_t("vxfs")
        vals += fs_u_t("ufs")
        vals += fs_u_zfs()
        return vars, vals

    def fs_u_t(t):
        if not which('df'):
            return []
        cmd = ['df', '-F', t, '-k']
        (out,err,ret) = justcall(cmd)
        if ret != 0:
            return []
        lines = out.split('\n')
        if len(lines) < 2:
            return []
        vals = []
        for line in lines[1:]:
            l = line.split()
            if len(l) == 5:
                l = [''] + l
            elif len(l) != 6:
                continue
            vals.append([now, node.nodename, l[5], l[1], l[4].replace('%','')])
        return vals

    def fs_u_zfs():
        if not which(rcEnv.syspaths.zfs):
            return []
        cmd = [rcEnv.syspaths.zfs, 'list', '-o', 'name,used,avail,mountpoint', '-H']
        (out,err,ret) = justcall(cmd)
        if ret != 0:
            return []
        lines = out.split('\n')
        if len(lines) == 0:
            return []
        vals = []
        for line in lines:
            l = line.split()
            if len(l) != 4:
                continue
            if "@" in l[0]:
                # do not report clone usage
                continue
            if "osvc_sync_" in l[0]:
                # do not report osvc sync snapshots fs usage
                continue
            used = convert(l[1])
            if l[2] == '0':
                l[2] = '0K'
            avail = convert(l[2])
            total = used + avail
            pct = used / total * 100
            vals.append([now, node.nodename, l[0], str(total), str(pct)])
        return vals

    def convert(s):
        s = s.replace(',', '.')
        if len(s) < 2:
            raise
        if s.endswith('T'):
            s = float(s[:-1])*1024*1024*1024
        elif s.endswith('G'):
            s = float(s[:-1])*1024*1024
        elif s.endswith('M'):
            s = float(s[:-1])*1024
        elif s.endswith('K'):
            s = float(s[:-1])
        else:
            raise
        return s

    node.collector.call('push_stats_fs_u', fs_u())
  0707010005c977000081b40000000000000000000000015ab2175400001260000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/rcStatsDarwin.py   import os
from rcUtilities import call, which
from rcGlobalEnv import rcEnv
import rcStats

class StatsProvider(rcStats.StatsProvider):
    def customfile(self, metric, day):
        f = os.path.join(rcEnv.paths.pathvar, 'stats', metric+day)
        if os.path.exists(f):
            return f
        return None

    def cpu(self, d, day, start, end):
        cols = ['date',
                'cpu',
                'usr',
                'nice',
                'sys',
                'idle',
                'nodename']
        f = self.sarfile(day)
        lines = []
        if f is None:
            return cols, lines
        cmd = ['sar', '-u', '-f', f, '-s', start, '-e', end]
        (ret, buff, err) = call(cmd, errlog=False)
        for line in buff.split('\n'):
            l = line.split()
            if len(l) != 5:
                continue
            if l[1] == '%usr':
                continue
            if l[0] == 'Average:':
                continue
            (time, usr, nice, sys, idle) = l
            l = ['%s %s'%(d, time), 'all', usr, nice, sys, idle, self.nodename]
            lines.append(l)
        return cols, lines

    def mem_u(self, d, day, start, end):
        cols = ['date',
                'kbmemfree',
                'kbmemused',
                'kbbuffers',
                'kbcached',
                'kbmemsys',
                'nodename']
        fname = self.customfile('mem_u', day)
        lines = []
        if fname is None:
            return cols, lines
        try:
            f = open(fname, 'r')
            buff = f.read()
            f.close()
        except:
            return cols, lines
        for line in buff.split('\n'):
            l = line.split()
            if len(l) != 6:
                continue
            (time, free, inactive, active, speculative, wired) = l
            l = ['%s %s'%(d, time), free, active, speculative, inactive, wired, self.nodename]
            lines.append(l)
        return cols, lines

    def blockdev(self, d, day, start, end):
        cols = ['date',
                'dev',
                'tps',
                'rsecps',
                'nodename']
        f = self.sarfile(day)
        lines = []
        if f is None:
            return cols, lines
        cmd = ['sar', '-d', '-f', f, '-s', start, '-e', end]
        (ret, buff, err) = call(cmd, errlog=False)
        for line in buff.split('\n'):
           l = line.split()
           if len(l) != 4:
               continue
           if l[1] == 'device':
               continue
           if l[1] == 'Disk:':
               continue
           if l[0] == 'Average:':
               continue
           l.append(self.nodename)
           l[0] = '%s %s'%(d, l[0])
           lines.append(l)
        return cols, lines

    def netdev(self, d, day, start, end):
        cols = ['date',
                'dev',
                'rxpckps',
                'rxkBps',
                'txpckps',
                'txkBps',
                'nodename']

        f = self.sarfile(day)
        lines = []
        if f is None:
            return cols, lines
        cmd = ['sar', '-n', 'DEV', '-f', f, '-s', start, '-e', end]
        (ret, buff, err) = call(cmd, errlog=False)
        for line in buff.split('\n'):
           l = line.split()
           if len(l) != 6:
               continue
           if l[1] in ['IFACE', 'lo0'] :
               continue
           if 'dummy' in l[1] or 'vnet' in l[1] or 'veth' in l[1] or \
              'gif'   in l[1] or 'stf'  in l[1]:
               continue
           if l[0] == 'Average:':
               continue
           l.append(self.nodename)
           l[0] = '%s %s'%(d, l[0])
           lines.append(l)
        return cols, lines


    def netdev_err(self, d, day, start, end):
        cols = ['date',
                'dev',
                'rxerrps',
                'txerrps',
                'collps',
                'rxdropps',
                'nodename']
        f = self.sarfile(day)
        lines = []
        if f is None:
            return cols, lines
        cmd = ['sar', '-n', 'EDEV', '-f', f, '-s', start, '-e', end]
        (ret, buff, err) = call(cmd, errlog=False)
        for line in buff.split('\n'):
           l = line.split()
           if len(l) != 6:
               continue
           if l[1] in ['IFACE', 'lo0'] :
               continue
           if 'dummy' in l[1] or 'vnet' in l[1] or 'veth' in l[1] or \
              'gif'   in l[1] or 'stf'  in l[1]:
               continue
           if l[0] == 'Average:':
               continue
           l.append(self.nodename)
           l[0] = '%s %s'%(d, l[0])
           lines.append(l)
        return cols, lines


0707010005c978000081b40000000000000000000000015ab217540000114d000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/rcStatsFreeBSD.py  import os
from rcUtilities import call, which
import rcStats

class StatsProvider(rcStats.StatsProvider):
    def cpu(self, d, day, start, end):
        cols = ['date',
                'usr',
                'sys',
                'nice',
                'irq',
                'idle',
                'cpu',
                'nodename']
        cmd = ['bsdsar', '-u', '-n', day]
        (ret, buff, err) = call(cmd, errlog=False)
        lines = []
        if ret != 0:
            return cols, lines
        for line in buff.split('\n'):
            l = line.split()
            if len(l) != 6:
                continue
            if l[0] == 'Time':
                continue
            l += ['ALL', self.nodename]
            l[0] = '%s %s'%(d, l[0])
            lines.append(l)
        return cols, lines

    def kb(self, s):
        n = int(s[0:-1])
        unit = s[-1]
        if unit == 'k' or unit =='K':
            return n
        elif unit == 'M':
            return n*1024
        elif unit == 'G':
            return n*1024*1024
        elif unit == 'T':
            return n*1024*1024*1204
        elif unit == 'P':
            return n*1024*1024*1204*1024

    def mem_u(self, d, day, start, end):
        cols = ['date',
                'kbmemfree',
                'kbmemused',
                'pct_memused',
                'kbmemsys',
                'nodename']

        cmd = ['sysctl', 'hw.physmem']
        (ret, out, err) = call(cmd)
        physmem = int(out.split(': ')[1])/1024

        cmd = ['sysctl', 'hw.usermem']
        (ret, out, err) = call(cmd)
        usermem = int(out.split(': ')[1])/1024

        cmd = ['bsdsar', '-r', '-n', day]
        (ret, buff, err) = call(cmd)
        lines = []
        for line in buff.split('\n'):
            l = line.split()
            if len(l) != 7:
                continue
            if l[0] == 'Time':
                continue
            free = self.kb(l[1])
            used = self.kb(l[2])+self.kb(l[3])
            x = [l[0], str(free), str(used), str(used/(used+free)), str(physmem-usermem), self.nodename]
            x[0] = '%s %s'%(d, x[0])
            lines.append(x)
        return cols, lines

    def swap(self, d, day, start, end):
        cols = ['date',
                'kbswpfree',
                'kbswpused',
                'pct_swpused',
                'kbswpcad',
                'pct_swpcad',
                'nodename']
        cmd = ['bsdsar', '-r', '-n', day]
        (ret, buff, err) = call(cmd, errlog=False)
        lines = []
        if ret != 0:
            return cols, lines
        for line in buff.split('\n'):
           l = line.split()
           if len(l) != 7:
               continue
           if l[0] == 'Time':
               continue
           free = self.kb(l[6])
           used = self.kb(l[5])
           x = [l[0], str(free), str(used), str(used/(free+used)), '0', '0']
           x.append(self.nodename)
           x[0] = '%s %s'%(d, x[0])
           lines.append(x)
        return cols, lines

    def netdev(self, d, day, start, end):
        cols = ['date',
                'rxpckps',
                'rxkBps',
                'txpckps',
                'txkBps',
                'dev',
                'nodename']
        cmd = ['bsdsar', '-I', '-n', day]
        (ret, buff, err) = call(cmd, errlog=False)
        lines = []
        if ret != 0:
            return cols, lines
        for line in buff.split('\n'):
           l = line.split()
           if len(l) != 9:
               continue
           if l[0] == 'Time':
               continue
           x = [l[0], l[1], l[3], l[4], l[6], l[8], self.nodename]
           x[0] = '%s %s'%(d, x[0])
           lines.append(x)
        return cols, lines

    def netdev_err(self, d, day, start, end):
        cols = ['date',
                'rxerrps',
                'txerrps',
                'collps',
                'dev',
                'nodename']
        cmd = ['bsdsar', '-I', '-n', day]
        (ret, buff, err) = call(cmd, errlog=False)
        lines = []
        if ret != 0:
            return cols, lines
        for line in buff.split('\n'):
           l = line.split()
           if len(l) != 9:
               continue
           if l[0] == 'Time':
               continue
           x = [l[0], l[2], l[5], l[7], l[8], self.nodename]
           x[0] = '%s %s'%(d, l[0])
           lines.append(x)
        return cols, lines

   0707010005c979000081b40000000000000000000000015ab2175400001a9d000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/rcStatsHP-UX.py    import os
from rcUtilities import call
from rcGlobalEnv import rcEnv
import rcStats

class StatsProvider(rcStats.StatsProvider):
    def glancefile(self, day):
        f = os.path.join(rcEnv.paths.pathvar, 'stats', 'glance'+day)
        if os.path.exists(f):
            return f
        return None

    def cpu(self, d, day, start, end):
        f = self.glancefile(day)
        if f is None:
            return [], []
        cols = ['date',
                'cpu',
                'usr',
                'nice',
                'sys',
                'iowait',
                'steal',
                'irq',
                'soft',
                'guest',
                'idle',
                'nodename']
        lines = []
        with open(f, 'r') as file:
            for line in file:
                l = line.split()
                if len(l) != 24:
                    continue
                """ hpux:            usr nice sys irq wait idle
                                     1   2    3   4   5    6
                    xmlrpc: date cpu usr nice sys iowait steal irq soft guest idle nodename
                """
                ts = '%s %s'%(d, l[0])
                ts = ts.replace('\0','')
                x = [ts,
                     'all',
                     l[1],
                     l[2],
                     l[3],
                     l[5],
                     '0',
                     l[4],
                     '0',
                     '0',
                     l[6],
                     self.nodename]
                lines.append(x)
            return cols, lines

    def mem_u(self, d, day, start, end):
        f = self.glancefile(day)
        if f is None:
            return [], []
        cols = ['date',
                'kbmemfree',
                'kbmemused',
                'pct_memused',
                'kbbuffers',
                'kbcached',
                'kbcommit',
                'pct_commit',
                'kbmemsys',
                'nodename']
        lines = []
        with open(f, 'r') as file:
            for line in file:
                l = line.split()
                if len(l) != 24:
                    continue
                """ hpux:            phys kbmemfree kbcached kbfilecached kbsys kbuser kbswapused kbswap
                                     7    8         9        10           11    12     13         14
                    xmlrpc: date kbmemfree kbmemused pct_memused kbbuffers kbcached kbcommit pct_commit kbmemsys nodename
                """
                phys = int(l[7])
                free = int(l[8])
                swapused = int(l[13])
                swap = int(l[14])
                used = phys - free
                commit = used + swapused
                vm = phys + swap
                if vm == 0 or phys == 0:
                    continue
                pct_commit = 100 * commit / vm
                pct_used = 100 * used / phys

                ts = '%s %s'%(d, l[0])
                ts = ts.replace('\0','')
                x = [ts,
                     l[8],
                     str(used),
                     str(pct_used),
                     l[9],
                     l[10],
                     str(commit),
                     str(pct_commit),
                     l[11],
                     self.nodename]
                lines.append(x)
        return cols, lines

    def proc(self, d, day, start, end):
        f = self.glancefile(day)
        if f is None:
            return [], []
        cols = ['date',
                'runq_sz',
                'plist_sz',
                'ldavg_1',
                'ldavg_5',
                'ldavg_15',
                'nodename']
        lines = []
        with open(f, 'r') as file:
            for line in file.readlines():
                l = line.split()
                if len(l) != 24:
                    continue
                """ hpux:            GBL_LOADAVG GBL_LOADAVG5 GBL_LOADAVG15 GBL_CPU_QUEUE TBL_PROC_TABLE_USED
                                     15          16           17            18            19
                    xmlrpc: date runq_sz plist_sz ldavg_1 ldavg_5 ldavg_15 nodename
                """
                ts = '%s %s'%(d, l[0])
                ts = ts.replace('\0','')
                x = [ts,
                     l[18],
                     l[19],
                     l[15],
                     l[16],
                     l[17],
                     self.nodename]
                lines.append(x)
        return cols, lines

    def swap(self, d, day, start, end):
        f = self.glancefile(day)
        if f is None:
            return [], []
        lines = []
        cols = ['date',
                'kbswpfree',
                'kbswpused',
                'pct_swpused',
                'kbswpcad',
                'pct_swpcad',
                'nodename']

        with open(f, 'r') as file:
            for line in file.readlines():
                l = line.split()
                if len(l) != 24:
                    continue
                """ hpux:        kbswapused kbswap
                                 13         14
                    xmlrpc: date kbswpfree kbswpused pct_swpused kbswpcad pct_swpcad nodename
                """
                swapused = int(l[13])
                swap = int(l[14])
                swapfree = swap - swapused

                ts = '%s %s'%(d, l[0])
                ts = ts.replace('\0','')
                x = [ts,
                     str(swapfree),
                     l[13],
                     str(100 * swapused / swap),
                     '0',
                     '0',
                     self.nodename]
                lines.append(x)
        return cols, lines

    def block(self, d, day, start, end):
        f = self.glancefile(day)
        if f is None:
            return [], []
        cols = ['date',
                'tps',
                'rtps',
                'wtps',
                'rbps',
                'wbps',
                'nodename']

        lines = []
        with open(f, 'r') as file:
            for line in file.readlines():
                l = line.split()
                if len(l) != 24:
                    continue
                """ hpux:        rio wio rkb wkb
                                 20  21  22  23
                    xmlrpc: date tps rtps wtps rbps wbps nodename
                """
                tps = float(l[20]) + float(l[21])
                ts = '%s %s'%(d, l[0])
                ts = ts.replace('\0','')
                x = [ts,
                     str(tps),
                     l[20],
                     l[21],
                     l[22],
                     l[23],
                     self.nodename]
                lines.append(x)
        return cols, lines

   0707010005c97a000081b40000000000000000000000015ab21754000035c5000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/rcStatsLinux.py    import os
import datetime
from rcUtilities import justcall, which
import rcStats
from rcGlobalEnv import rcEnv

class StatsProvider(rcStats.StatsProvider):
    def xentopfile(self, day):
        f = os.path.join(rcEnv.paths.pathlog, 'xentop', 'xentop'+day)
        if os.path.exists(f):
            return f
        return None

    def svc(self, d, day, start, end):
        cols = ['date',
                'svcname',
                'cpu',
                'mem',
                'cap',
                'cap_cpu',
                'nodename']
        f = self.xentopfile(day)
        lines = []
        if f is None:
            return cols, lines
        try:
            with open(f, 'r') as f:
                buff = f.read()
        except:
            return cols, lines
        _start = datetime.datetime.strptime(start, "%H:%M:%S")
        _start = _start.hour * 3600 + _start.minute * 60 + _start.second
        _end = datetime.datetime.strptime(end, "%H:%M:%S")
        _end = _end.hour * 3600 + _end.minute * 60 + _end.second
        for line in buff.split('\n'):
            l = line.split()
            if len(l) != 21:
                continue
            _d = datetime.datetime.strptime(" ".join(l[0:2]), "%Y-%m-%d %H:%M:%S")
            _d = _d.hour * 3600 + _d.minute * 60 + _d.second
            if _d < _start or _d > _end:
                continue
            l = [" ".join((l[0], l[1]))] + [l[2], l[5], l[7], l[8], l[10], self.nodename]
            lines.append(l)
        return cols, lines

    def cpu(self, d, day, start, end):
        f = self.sarfile(day)
        if f is None:
            return [], []
        cmd = ['sar', '-t', '-u', 'ALL', '-P', 'ALL', '-f', f, '-s', start, '-e', end]
        (buff, err, ret) = justcall(cmd)
        if ret != 0:
            cmd = ['sar', '-t', '-u', '-P', 'ALL', '-f', f, '-s', start, '-e', end]
            (buff, err, ret) = justcall(cmd)
        cols = []
        lines = []
        for line in buff.split('\n'):
            l = line.split()
            if 'Linux' in l:
                continue
            if len(l) == 7:
                """ redhat 4
                    18:50:01 CPU %user %nice %system %iowait %idle
                """
                cols = ['date',
                        'cpu',
                        'usr',
                        'nice',
                        'sys',
                        'iowait',
                        'idle',
                        'nodename']
            elif len(l) == 8:
                """ redhat 5
                    05:20:01 CPU %user %nice %system %iowait %steal %idle
                """
                cols = ['date',
                        'cpu',
                        'usr',
                        'nice',
                        'sys',
                        'iowait',
                        'steal',
                        'idle',
                        'nodename']
            elif len(l) == 11:
                cols = ['date',
                        'cpu',
                        'usr',
                        'nice',
                        'sys',
                        'iowait',
                        'steal',
                        'irq',
                        'soft',
                        'guest',
                        'idle',
                        'nodename']
            elif len(l) == 12:
                cols = ['date',
                        'cpu',
                        'usr',
                        'nice',
                        'sys',
                        'iowait',
                        'steal',
                        'irq',
                        'soft',
                        'guest',
                        'gnice',
                        'idle',
                        'nodename']
            else:
                continue
            if l[1] == 'CPU':
                continue
            if l[0] == 'Average:':
                continue
            l.append(self.nodename)
            l[0] = '%s %s'%(d, l[0])
            lines.append(l)
        return cols, lines

    def mem_u(self, d, day, start, end):
        f = self.sarfile(day)
        if f is None:
            return [], []
        cmd = ['sar', '-t', '-r', '-f', f, '-s', start, '-e', end]
        buff, err, ret = justcall(cmd)

        if "kbdirty" in buff:
            fmt = 4
            cols = ['date',
                    'kbmemfree',
                    'kbmemused',
                    'pct_memused',
                    'kbbuffers',
                    'kbcached',
                    'kbcommit',
                    'pct_commit',
                    'kbactive',
                    'kbinact',
                    'kbdirty',
                    'nodename']
        elif "kbactive" in buff:
            fmt = 3
            cols = ['date',
                    'kbmemfree',
                    'kbmemused',
                    'pct_memused',
                    'kbbuffers',
                    'kbcached',
                    'kbcommit',
                    'pct_commit',
                    'kbactive',
                    'kbinact',
                    'nodename']
        elif "pct_commit" in buff:
            fmt = 2
            cols = ['date',
                    'kbmemfree',
                    'kbmemused',
                    'pct_memused',
                    'kbbuffers',
                    'kbcached',
                    'kbcommit',
                    'pct_commit',
                    'nodename']
        else:
            fmt = 1
            cols = ['date',
                    'kbmemfree',
                    'kbmemused',
                    'pct_memused',
                    'kbbuffers',
                    'kbcached',
                    'nodename']

        n = len(cols) - 1
        lines = []
        for line in buff.split('\n'):
           l = line.split()
           if fmt > 1:
               if len(l) != n:
                   continue
           else:
               if len(l) < n:
                   continue
               l = l[:n]
           if l[1] == 'kbmemfree':
               continue
           if l[0] == 'Average:':
               continue

           l.append(self.nodename)
           l[0] = '%s %s'%(d, l[0])
           lines.append(l)
        return cols, lines

    def fs_u(self, d, day, start, end):
        now = datetime.datetime.now()
        _start = datetime.datetime.strptime(start, "%H:%M:%S")
        _start = _start.hour * 3600 + _start.minute * 60 + _start.second
        _end = datetime.datetime.strptime(end, "%H:%M:%S")
        _end = _end.hour * 3600 + _end.minute * 60 + _end.second
        d = os.path.join(rcEnv.paths.pathvar, 'stats')
        f = os.path.join(d, 'fs_u.%s' % day.lstrip("0"))
        cols = ['date',
                'nodename',
                'mntpt',
                'size',
                'used']

        if not os.path.exists(d):
            return [], []
        if not os.path.exists(f):
            return [], []

        with open(f, 'r') as fd:
            buff = fd.read()

        import json
        lines = []
        for line in buff.split('\n'):
            try:
                l = json.loads(line)
            except:
                continue
            for _l in l:
                if len(_l) != 5:
                    continue
                _now = datetime.datetime.strptime(_l[0], "%Y-%m-%d %H:%M:%S.%f")
                _now = _now.hour * 3600 + _now.minute * 60 + _now.second
                if _now < _start or _now > _end:
                    continue
                lines.append(_l)

        return cols, lines

    def proc(self, d, day, start, end):
        f = self.sarfile(day)
        cols = ['date',
                'runq_sz',
                'plist_sz',
                'ldavg_1',
                'ldavg_5',
                'ldavg_15',
                'nodename']

        if f is None:
            return [], []
        cmd = ['sar', '-t', '-q', '-f', f, '-s', start, '-e', end]
        (buff, err, ret) = justcall(cmd)
        lines = []
        if "blocked" in buff:
            n_fields = 7
            drop_blocked = True
        else:
            n_fields = 6
            drop_blocked = False
        for line in buff.split('\n'):
           l = line.split()
           if len(l) != n_fields:
               continue
           if l[1] == 'runq-sz':
               continue
           if l[0] == 'Average:':
               continue
           if drop_blocked:
              l = l[:-1]
           l.append(self.nodename)
           l[0] = '%s %s'%(d, l[0])
           lines.append(l)
        return cols, lines

    def swap(self, d, day, start, end):
        f = self.sarfile(day)
        cols = ['date',
                'kbswpfree',
                'kbswpused',
                'pct_swpused',
                'kbswpcad',
                'pct_swpcad',
                'nodename']

        if f is None:
            return [], []
        cmd = ['sar', '-t', '-S', '-f', f, '-s', start, '-e', end]
        (buff, err, ret) = justcall(cmd)
        if ret != 0:
            """ redhat 5
            """
            cmd = ['sar', '-t', '-r', '-f', f, '-s', start, '-e', end]
            (buff, err, ret) = justcall(cmd)
        lines = []
        for line in buff.split('\n'):
           l = line.split()
           if len(l) == 10:
               """ redhat 5
               """
               l = [l[0]] + l[6:] + ['0']
           if len(l) != 6:
               continue
           if 'kbswpfree'in l:
               continue
           if l[0] == 'Average:':
               continue
           l.append(self.nodename)
           l[0] = '%s %s'%(d, l[0])
           lines.append(l)
        return cols, lines

    def block(self, d, day, start, end):
        f = self.sarfile(day)
        cols = ['date',
                'tps',
                'rtps',
                'wtps',
                'rbps',
                'wbps',
                'nodename']

        if f is None:
            return [], []
        cmd = ['sar', '-t', '-b', '-f', f, '-s', start, '-e', end]
        (buff, err, ret) = justcall(cmd)
        lines = []
        for line in buff.split('\n'):
           l = line.split()
           if len(l) != 6:
               continue
           if l[1] == 'tps':
               continue
           if l[0] == 'Average:':
               continue
           l.append(self.nodename)
           l[0] = '%s %s'%(d, l[0])
           lines.append(l)
        return cols, lines

    def blockdev(self, d, day, start, end):
        f = self.sarfile(day)
        cols = ['date',
                'dev',
                'tps',
                'rsecps',
                'wsecps',
                'avgrq_sz',
                'avgqu_sz',
                'await',
                'svctm',
                'pct_util',
                'nodename']
        if f is None:
            return [], []
        cmd = ['sar', '-t', '-d', '-p', '-f', f, '-s', start, '-e', end]
        (buff, err, ret) = justcall(cmd)
        lines = []
        for line in buff.split('\n'):
           l = line.split()
           if len(l) != 10:
               continue
           if l[1] == 'DEV':
               continue
           if l[0] == 'Average:':
               continue
           l.append(self.nodename)
           l[0] = '%s %s'%(d, l[0])
           lines.append(l)
        return cols, lines

    def netdev(self, d, day, start, end):
        f = self.sarfile(day)
        cols = ['date',
                'dev',
                'rxpckps',
                'txpckps',
                'rxkBps',
                'txkBps',
                'nodename']

        if f is None:
            return [], []

        cmd = ['sar', '-t', '-n', 'DEV', '-f', f, '-s', start, '-e', end]
        (buff, err, ret) = justcall(cmd)

        if "%ifutil" in buff:
            n = 10
        else:
            n = 9

        lines = []
        div = 1
        for line in buff.split('\n'):
           l = line.split()
           if len(l) != n:
               continue
           if l[1] in ['IFACE', 'lo'] :
               if 'rxbyt/s' in l:
                   div = 1024
               continue
           if 'dummy' in l[1] or 'vnet' in l[1] or 'veth' in l[1] or \
              'pan'   in l[1] or 'sit'  in l[1]:
               continue
           if l[0] == 'Average:':
               continue
           m = []
           m.append('%s %s'%(d, l[0]))
           m.append(l[1])
           m.append(str(float(l[4])/div))
           m.append(str(float(l[5])/div))
           m.append(l[2])
           m.append(l[3])
           m.append(self.nodename)
           lines.append(m)
        return cols, lines


    def netdev_err(self, d, day, start, end):
        f = self.sarfile(day)
        cols = ['date',
                'dev',
                'rxerrps',
                'txerrps',
                'collps',
                'rxdropps',
                'txdropps',
                'nodename']

        if f is None:
            return [], []
        cmd = ['sar', '-t', '-n', 'EDEV', '-f', f, '-s', start, '-e', end]
        (buff, err, ret) = justcall(cmd)
        lines = []
        for line in buff.split('\n'):
           l = line.split()
           if len(l) != 11:
               continue
           if l[1] in ['IFACE', 'lo'] :
               continue
           if 'dummy' in l[1] or 'vnet' in l[1] or 'veth' in l[1] or \
              'pan'   in l[1] or 'sit'  in l[1]:
               continue
           if l[0] == 'Average:':
               continue
           l = l[0:7]
           l.append(self.nodename)
           l[0] = '%s %s'%(d, l[0])
           lines.append(l)
        return cols, lines

if __name__ == "__main__":
    sp = StatsProvider(interval=200)
    print(sp.get('mem_u'))
   0707010005c97b000081b40000000000000000000000015ab2175400001ccc000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/rcStatsSunOS.py    import os
from rcUtilities import call, which
import rcStats
import datetime

class StatsProvider(rcStats.StatsProvider):
    def __init__(self, interval=2880, stats_dir=None,
                 stats_start=None, stats_end=None):
        rcStats.StatsProvider.__init__(self, interval=interval,
                                       stats_dir=stats_dir,
                                       stats_start=stats_start,
                                       stats_end=stats_end)
        cmd = ['pagesize']
        (ret, pagesize, err) = call(cmd)
        self.pagesize = int(pagesize)

    def zsfile(self, day):
        f = os.path.join(os.sep, 'var', 'adm', 'zonestat', 'zs'+day)
        if os.path.exists(f):
            return f
        return None

    def sarfile(self, day):
        f = os.path.join(os.sep, 'var', 'adm', 'sa', 'sa'+day)
        if os.path.exists(f):
            return f
        return None

    def svc(self, d, day, start, end):
        cols = ['date',
                'svcname',
                'swap',
                'rss',
                'cap',
                'at',
                'avgat',
                'pg',
                'avgpg',
                'nproc',
                'mem',
                'cpu',
                'nodename']
        f = self.zsfile(day)
        lines = []
        if f is None:
            return cols, lines
        try:
            with open(f, 'r') as f:
                buff = f.read()
        except:
            return cols, lines
        _start = datetime.datetime.strptime(start, "%H:%M:%S")
        _start = _start.hour * 3600 + _start.minute * 60 + _start.second
        _end = datetime.datetime.strptime(end, "%H:%M:%S")
        _end = _end.hour * 3600 + _end.minute * 60 + _end.second
        for line in buff.split('\n'):
            l = line.split()
            if len(l) != 17:
                continue
            _d = datetime.datetime.strptime(" ".join(l[0:2]), "%Y-%m-%d %H:%M:%S")
            _d = _d.hour * 3600 + _d.minute * 60 + _d.second
            if _d < _start or _d > _end:
                continue
            for i, e in enumerate(l):
                if e.endswith('T'):
                    l[i] = str(int(e[0:-1]) * 1024 * 1024)
                elif e.endswith('G'):
                    l[i] = str(int(e[0:-1]) * 1024)
                elif e.endswith('M'):
                    l[i] = e.rstrip('M')
                elif e.endswith('K'):
                    l[i] = str(1.0 * int(e[0:-1]) / 1024)
            l = [" ".join(l[0:2])] + l[2:-4] + [self.nodename]
            lines.append(l)
        return cols, lines

    def cpu(self, d, day, start, end):
        cols = ['date',
                'usr',
                'sys',
                'iowait',
                'idle',
                'cpu',
                'nodename']
        f = self.sarfile(day)
        lines = []
        if f is None:
            return cols, lines
        cmd = ['sar', '-u', '-f', f, '-s', start, '-e', end]
        (ret, buff, err) = call(cmd, errlog=False)
        for line in buff.split('\n'):
            l = line.split()
            if len(l) != 5:
                continue
            if l[1] == '%usr':
                continue
            if l[0] == 'Average':
                continue
            l += ['all', self.nodename]
            l[0] = '%s %s'%(d, l[0])
            lines.append(l)
        return cols, lines

    def mem_u(self, d, day, start, end):
        cols = ['date',
                'kbmemfree',
                'nodename']
        f = self.sarfile(day)
        lines = []
        if f is None:
            return cols, lines
        cmd = ['sar', '-r', '-f', f, '-s', start, '-e', end]
        (ret, buff, err) = call(cmd)
        for line in buff.split('\n'):
            l = line.split()
            if len(l) != 3:
                continue
            if l[1] == 'freemem':
                continue
            if l[0] == 'Average':
                continue

            try:
                freemem = int(l[1])*self.pagesize/1024
            except:
                continue
            x = ['%s %s'%(d, l[0]), str(freemem), self.nodename]
            lines.append(x)
        return cols, lines

    def proc(self, d, day, start, end):
        cols = ['date',
                'runq_sz',
                'nodename']
        f = self.sarfile(day)
        lines = []
        if f is None:
            return cols, lines
        cmd = ['sar', '-q', '-f', f, '-s', start, '-e', end]
        (ret, buff, err) = call(cmd)
        for line in buff.split('\n'):
            l = line.split()
            if len(l) != 5:
                continue
            if l[1] == 'runq-sz':
                continue
            if l[0] == 'Average':
                continue
            x = ['%s %s'%(d, l[0]), l[1], self.nodename]
            lines.append(x)
        return cols, lines

    def swap(self, d, day, start, end):
        cols = ['date',
                'kbswpfree',
                'nodename']
        f = self.sarfile(day)
        lines = []
        if f is None:
            return cols, lines
        cmd = ['sar', '-r', '-f', f, '-s', start, '-e', end]
        (ret, buff, err) = call(cmd)
        for line in buff.split('\n'):
            l = line.split()
            if len(l) != 3:
                continue
            if l[1] == 'freemem':
                continue
            if l[0] == 'Average':
                continue

            try:
                freeswap = int(l[2])/2
            except:
                continue
            x = ['%s %s'%(d, l[0]), str(freeswap), self.nodename]
            lines.append(x)
        return cols, lines

    def block(self, d, day, start, end):
        cols = ['date',
                'rbps',
                'wbps',
                'nodename']
        f = self.sarfile(day)
        lines = []
        if f is None:
            return [], []
        cmd = ['sar', '-b', '-f', f, '-s', start, '-e', end]
        (ret, buff, err) = call(cmd)
        for line in buff.split('\n'):
            l = line.split()
            if len(l) != 9:
                continue
            if l[1] == 'bread/s':
                continue
            if l[0] == 'Average':
                continue
            x = ['%s %s'%(d, l[0]), l[1], l[4], self.nodename]
            lines.append(x)
        return cols, lines

    def blockdev(self, d, day, start, end):
        cols = ['date',
                'dev',
                'pct_util',
                'avgqu_sz',
                'rsecps',
                'await',
                'svctm',
                'nodename']
        f = self.sarfile(day)
        lines = []
        if f is None:
            return cols, lines
        cmd = ['sar', '-d', '-f', f, '-s', start, '-e', end]
        (ret, buff, err) = call(cmd, errlog=False)
        for line in buff.split('\n'):
            l = line.split()
            if len(l) == 8:
                date = l[0]
            if len(l) == 7:
                l = [date] + l
            if len(l) != 8:
                continue
            if l[1] == 'device':
                continue
            if l[0] == 'Average':
                continue
            # 00:00:00 device %busy avque r+w/s [blks/s] avwait avserv
            x = ['%s %s'%(d, l[0]), l[1], l[2], l[3], l[4], l[6], l[7], self.nodename]
            lines.append(x)
        return cols, lines

0707010005c97c000081b40000000000000000000000015ab21754000017b9000000660000000000000000000000000000002700000000root/usr/share/opensvc/lib/rcStatus.py    """
This module defines the Status class and the functions
to convert a Status to its printable form or integer form.
"""
from rcColor import color, colorize

UP = 0
DOWN = 1
WARN = 2
NA = 3
UNDEF = 5
STDBY_UP = 6
STDBY_DOWN = 7
STDBY_UP_WITH_UP = 8
STDBY_UP_WITH_DOWN = 9

STATUS_VALUE = {
    'up': UP,
    'down': DOWN,
    'warn': WARN,
    'n/a': NA,
    'na': NA,
    'undef': UNDEF,
    'stdby up': STDBY_UP,
    'stdby down': STDBY_DOWN,
}

STATUS_STR = {
    UP: 'up',
    DOWN: 'down',
    WARN: 'warn',
    NA: 'n/a',
    UNDEF: 'undef',
    STDBY_UP: 'stdby up',
    STDBY_DOWN: 'stdby down',
    STDBY_UP_WITH_UP: 'up',
    STDBY_UP_WITH_DOWN: 'stdby up',
}

def encode_pair(status1, status2):
    """
    Return a hashable code unique for the set([status1, status2]).
    """
    return (1 << status1) | (1 << status2)

MERGE_RULES = {
    encode_pair(UP, UP): UP,
    encode_pair(UP, DOWN): WARN,
    encode_pair(UP, WARN): WARN,
    encode_pair(UP, NA): UP,
    encode_pair(UP, STDBY_UP): STDBY_UP_WITH_UP,
    encode_pair(UP, STDBY_DOWN): WARN,
    encode_pair(UP, STDBY_UP_WITH_UP): STDBY_UP_WITH_UP,
    encode_pair(UP, STDBY_UP_WITH_DOWN): WARN,
    encode_pair(DOWN, DOWN): DOWN,
    encode_pair(DOWN, WARN): WARN,
    encode_pair(DOWN, NA): DOWN,
    encode_pair(DOWN, STDBY_UP): STDBY_UP_WITH_DOWN,
    encode_pair(DOWN, STDBY_DOWN): STDBY_DOWN,
    encode_pair(DOWN, STDBY_UP_WITH_UP): WARN,
    encode_pair(DOWN, STDBY_UP_WITH_DOWN): STDBY_UP_WITH_DOWN,
    encode_pair(WARN, WARN): WARN,
    encode_pair(WARN, NA): WARN,
    encode_pair(WARN, STDBY_UP): WARN,
    encode_pair(WARN, STDBY_DOWN): WARN,
    encode_pair(WARN, STDBY_UP_WITH_UP): WARN,
    encode_pair(WARN, STDBY_UP_WITH_DOWN): WARN,
    encode_pair(NA, NA): NA,
    encode_pair(NA, STDBY_UP): STDBY_UP,
    encode_pair(NA, STDBY_DOWN): STDBY_DOWN,
    encode_pair(NA, STDBY_UP_WITH_UP): STDBY_UP_WITH_UP,
    encode_pair(NA, STDBY_UP_WITH_DOWN): STDBY_UP_WITH_DOWN,
    encode_pair(STDBY_UP, STDBY_UP): STDBY_UP,
    encode_pair(STDBY_UP, STDBY_DOWN): WARN,
    encode_pair(STDBY_UP, STDBY_UP_WITH_UP): STDBY_UP_WITH_UP,
    encode_pair(STDBY_UP, STDBY_UP_WITH_DOWN): STDBY_UP_WITH_DOWN,
    encode_pair(STDBY_DOWN, STDBY_DOWN): STDBY_DOWN,
    encode_pair(STDBY_DOWN, STDBY_UP_WITH_UP): WARN,
    encode_pair(STDBY_DOWN, STDBY_UP_WITH_DOWN): WARN,
    encode_pair(STDBY_UP_WITH_UP, STDBY_UP_WITH_DOWN): WARN,
    encode_pair(STDBY_UP_WITH_UP, STDBY_UP_WITH_UP): STDBY_UP_WITH_UP,
    encode_pair(STDBY_UP_WITH_DOWN, STDBY_UP_WITH_DOWN): STDBY_UP_WITH_DOWN,
}

def colorize_status(status, lpad=10):
    """
    Return the colorized human readable status string.
    """
    if isinstance(status, Status):
        status = str(status)
    elif isinstance(status, int):
        status = str(Status(status))

    fmt = "%-"+str(lpad)+"s"
    if status is None:
        return colorize(fmt % "undef", color.LIGHTBLUE)
    elif status == "warn":
        return colorize(fmt % status, color.BROWN)
    elif status.endswith("down") or status in ("err", "error"):
        return colorize(fmt % status, color.RED)
    elif status.endswith("up") or status == "ok":
        return colorize(fmt % status, color.GREEN)
    elif status == "n/a":
        return colorize(fmt % status, color.LIGHTBLUE)
    else:
        return colorize(fmt % status, color.LIGHTBLUE)
    return fmt % status

def status_value(status):
    """
    Return the machine readable status integer code.
    """
    if status not in STATUS_VALUE:
        return
    return STATUS_VALUE[status.lower()]

def status_str(val):
    """
    Return the human readable status string.
    """
    if val not in STATUS_STR:
        return
    return STATUS_STR[val]

class Status(object):
    """
    Class that wraps printing and calculation of resource status
    """
    @staticmethod
    def _merge(status1, status2):
        """
        Merge two status: WARN taints UP and DOWN
        """
        if status1 not in STATUS_STR:
            raise Exception("left member has unsupported value: %s" % str(status1))
        elif status2 not in STATUS_STR:
            raise Exception("right member has unsupported value: %s" % str(status2))

        if status1 == UNDEF:
            return status2
        elif status2 == UNDEF:
            return status1

        setstate = encode_pair(status1, status2)
        if setstate not in MERGE_RULES:
            raise Exception("some member has unsupported value: %s , %s " % \
                            (str(status1), str(status2)))
        return MERGE_RULES[setstate]

    def value(self):
        """
        Return the integer status code.
        """
        return self.status

    def reset(self):
        """
        Reset the status to 'undef'.
        """
        self.status = UNDEF

    def __hash__(self):
        return hash(self.status)

    def __add__(self, other):
        self.status = self._merge(self.status, other.status)
        return self

    def __iadd__(self, other):
        if isinstance(other, Status):
            self.status = self._merge(self.status, other.status)
        else:
            self.status = self._merge(self.status, other)
        return self

    def __eq__(self, other):
        if isinstance(other, Status):
            return self.status == other.status
        try:
            other = int(other)
            return self.status == other
        except (ValueError, TypeError):
            pass
        return str(self) == other

    def __int__(self):
        return self.status

    def __str__(self):
        return status_str(self.status)

    def __init__(self, initial_status=None):
        if isinstance(initial_status, Status):
            self.status = initial_status.status
        elif isinstance(initial_status, int):
            self.status = initial_status
        elif initial_status is None:
            self.status = UNDEF
        else:
            try:
                self.status = int(initial_status)
            except (ValueError, TypeError):
                self.status = STATUS_VALUE[str(initial_status)]
   0707010005c97d000081b40000000000000000000000015ab2175400009f1d000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/rcSymmetrix.py from __future__ import print_function

import logging
import sys
import os
import json
import time
from xml.etree.ElementTree import XML, fromstring

try:
    import ConfigParser
except ImportError:
    import configparser as ConfigParser

import rcExceptions as ex
from rcGlobalEnv import rcEnv, Storage
from rcUtilities import justcall, which
from converters import convert_size
from rcOptParser import OptParser
from optparse import Option

PROG = "nodemgr array"
OPT = Storage({
    "help": Option(
        "-h", "--help", action="store_true", dest="parm_help",
        help="show this help message and exit"),
    "array": Option(
        "-a", "--array", action="store", dest="array_name",
        help="The name of the array, as defined in auth.conf"),
    "name": Option(
        "--name", action="store", dest="name",
        help="The device identifier name (ex: mysvc_1)"),
    "dev": Option(
        "--dev", action="store", dest="dev",
        help="The device id (ex: 00A04)"),
    "force": Option(
        "--force", action="store_true", dest="force",
        help="bypass the downsize sanity check."),
    "pair": Option(
        "--pair", action="store", dest="pair",
        help="The device id pair (ex: 00A04:00A04)"),
    "mappings": Option(
        "--mappings", action="append", dest="mappings",
        help="A <hba_id>:<tgt_id>,<tgt_id>,... mapping used in add map in replacement of --targetgroup and --initiatorgroup. Can be specified multiple times."),
    "size": Option(
        "--size", action="store", dest="size",
        help="The disk size, expressed as a size expression like 1g, 100mib, ..."),
    "slo": Option(
        "--slo", action="store", dest="slo",
        help="The thin device Service Level Objective."),
    "srp": Option(
        "--srp", action="store", dest="srp",
        help="The Storage Resource Pool hosting the device."),
    "srdf": Option(
        "--srdf", action="store_true", dest="srdf",
        help="Create a SRDF mirrored device pair. The array pointed by --array is will host the R1 member."),
    "rdfg": Option(
        "--rdfg", action="store", dest="rdfg",
        help="The RDF / RA Group number, required if --srdf is set."),
    "sg": Option(
        "--sg", action="store", dest="sg",
        help="As an alternative to --mappings, specify the storage group to put the dev into."),
    "invalidate": Option(
        "--invalidate", action="store", dest="invalidate",
        help="The SRDF mirror member to invalidate upon createpair (ex: R2). Don't set to just establish."),
    "srdf_type": Option(
        "--srdf-type", action="store", dest="srdf_type",
        help="The device role in the SRDF mirror (ex: R1)"),
    "srdf_mode": Option(
        "--srdf-mode", action="store", dest="srdf_mode",
        help="Device mirroring mode. Either sync, acp_wp or acp_disk"),
})

GLOBAL_OPTS = [
    OPT.array,
]

DEPRECATED_ACTIONS = []

ACTIONS = {
    "Generic actions": {
        "add_disk": {
            "msg": "Add and present a thin device.",
            "options": [
                OPT.name,
                OPT.size,
                OPT.mappings,
                OPT.slo,
                OPT.srp,
                OPT.srdf,
                OPT.rdfg,
            ],
        },
        "add_map": {
            "msg": "Present a device.",
            "options": [
                OPT.dev,
                OPT.mappings,
                OPT.slo,
                OPT.srp,
                OPT.sg,
            ],
        },
        "del_disk": {
            "msg": "Unpresent and delete a thin device.",
            "options": [
                OPT.dev,
            ],
        },
        "del_map": {
            "msg": "Unpresent a device.",
            "options": [
                OPT.dev,
            ],
        },
        "rename_disk": {
            "msg": "Rename a device.",
            "options": [
                OPT.dev,
                OPT.name,
            ],
        },
        "resize_disk": {
            "msg": "Resize a thin device.",
            "options": [
                OPT.dev,
                OPT.force,
                OPT.size,
            ],
        },
    },
    "Low-level actions": {
        "add_tdev": {
            "msg": "Add a thin device. No masking.",
            "options": [
                OPT.name,
                OPT.size,
            ],
        },
        "createpair": {
            "msg": "Delete the SRDF pairing for device.",
            "options": [
                OPT.pair,
                OPT.rdfg,
                OPT.invalidate,
                OPT.srdf_mode,
                OPT.srdf_type,
            ],
        },
        "del_tdev": {
            "msg": "Delete a thin device. No unmasking.",
            "options": [
                OPT.dev,
            ],
        },
        "deletepair": {
            "msg": "Delete the SRDF pairing for device.",
            "options": [
                OPT.dev,
            ],
        },
        "list_pools": {
            "msg": "List thin pools.",
        },
        "list_sgs": {
            "msg": "List storage groups.",
        },
        "list_tdevs": {
            "msg": "List thin devices.",
            "options": [
                OPT.dev,
            ],
        },
        "list_views": {
            "msg": "List views, eg. groups of initiators/targets/devices.",
            "options": [
                OPT.dev,
            ],
        },
        "set_mode": {
            "msg": "Set the device pair rdf mode.",
            "options": [
                OPT.dev,
                OPT.srdf_mode,
            ],
        },
    },
}


class Arrays(object):
    arrays = []

    def find_symcli_path(self):
        symcli_bin = which("symcli")
        if symcli_bin is not None:
            return os.path.dirname(symcli_bin)
        symcli_bin = "/usr/symcli/bin/symcli"
        if os.path.exists(symcli_bin):
            return os.path.dirname(symcli_bin)
        symcli_bin = "/opt/emc/SYMCLI/bin/symcli"
        if os.path.exists(symcli_bin):
            return os.path.dirname(symcli_bin)

    def __init__(self, objects=[]):
        self.objects = objects
        if len(objects) > 0:
            self.filtering = True
        else:
            self.filtering = False
        cf = rcEnv.paths.authconf
        if not os.path.exists(cf):
            return
        conf = ConfigParser.RawConfigParser()
        conf.read(cf)

        self.symms = []
        for s in conf.sections():
            if self.filtering and s not in self.objects:
                continue

            try:
                stype = conf.get(s, 'type')
            except:
                continue
            if stype != "symmetrix":
                continue
            try:
                name = s
            except:
                print("error parsing section", s, file=sys.stderr)
                continue

            if conf.has_option(s, 'symcli_path'):
                symcli_path = conf.get(s, 'symcli_path')
            else:
                symcli_path = self.find_symcli_path()

            if symcli_path is None:
                print("symcli path not found for array", s, file=sys.stderr)
                continue

            if conf.has_option(s, 'symcli_connect'):
                symcli_connect = conf.get(s, 'symcli_connect')
                os.environ["SYMCLI_CONNECT"] = symcli_connect
            else:
                symcli_connect = None

            if conf.has_option(s, 'username'):
                username = conf.get(s, 'username')
            else:
                username = None

            if conf.has_option(s, 'password'):
                password = conf.get(s, 'password')
            else:
                password = None

            symcfg = os.path.join(symcli_path, "symcfg")
            if which(symcfg) is None:
                raise ex.excError('can not find symcfg in %s' % symcli_path)

            out, err, ret = justcall([symcfg, 'list', '-sid', name, '-output', 'xml_element'])
            if ret != 0:
                print(err, file=sys.stderr)
                continue
            tree = fromstring(out)
            for symm in tree.getiterator('Symm_Info'):
                model = symm.find('model').text
                if model.startswith('VMAX'):
                    self.arrays.append(Vmax(name, symcli_path, symcli_connect, username, password))
                elif 'DMX' in model or '3000-M' in model:
                    self.arrays.append(Dmx(name, symcli_path, symcli_connect, username, password))
                else:
                    print("unsupported sym model: %s" % model, file=sys.stderr)

        del(conf)

    def get_array(self, name):
        for array in self.arrays:
            if array.sid == name:
                return array

    def __iter__(self):
        for array in self.arrays:
            yield(array)


class Sym(object):
    def __init__(self, sid, symcli_path, symcli_connect, username, password):
        self.keys = [
            'sym_info',
            'sym_dir_info',
            'sym_rdfg_info',
            'sym_dev_info',
            'sym_dev_wwn_info',
            'sym_dev_name_info',
            'sym_devrdfa_info',
            'sym_ficondev_info',
            'sym_meta_info',
            'sym_disk_info',
            'sym_diskgroup_info',
        ]
        self.sid = sid
        self.symcli_path = symcli_path
        self.symcli_connect = symcli_connect
        self.username = username
        self.password = password
        self.log = logging.getLogger(rcEnv.nodename+".array.sym."+self.sid)

    def set_environ(self):
        if self.symcli_connect:
            os.environ["SYMCLI_CONNECT"] = self.symcli_connect
        elif "SYMCLI_CONNECT" in os.environ:
            del os.environ["SYMCLI_CONNECT"]

    def symcmd(self, cmd, xml=True, log=False):
        self.set_environ()
        cmd += ['-sid', self.sid]
        if xml:
            cmd += ['-output', 'xml_element']
        if log and self.node:
            self.log.info(" ".join(cmd))
        return justcall(cmd)

    def symsg(self, cmd, xml=True, log=False):
        cmd = ['/usr/symcli/bin/symsg'] + cmd
        return self.symcmd(cmd, xml=xml, log=log)

    def symcfg(self, cmd, xml=True, log=False):
        cmd = ['/usr/symcli/bin/symcfg'] + cmd
        return self.symcmd(cmd, xml=xml, log=log)

    def symdisk(self, cmd, xml=True):
        cmd = ['/usr/symcli/bin/symdisk'] + cmd
        return self.symcmd(cmd, xml=xml)

    def symconfigure(self, cmd, xml=True, log=False):
        cmd = ['/usr/symcli/bin/symconfigure'] + cmd
        return self.symcmd(cmd, xml=xml, log=log)

    def symdev(self, cmd, xml=True, log=False):
        cmd = ['/usr/symcli/bin/symdev'] + cmd
        return self.symcmd(cmd, xml=xml, log=log)

    def symrdf(self, cmd, xml=True, log=False):
        cmd = ['/usr/symcli/bin/symrdf'] + cmd
        return self.symcmd(cmd, xml=xml, log=log)

    def get_sym_info(self):
        out, err, ret = self.symcfg(["list"])
        return out

    def get_sym_rdfg_info(self):
        out, err, ret = self.symcfg(['-rdfg', 'all', 'list'])
        return out

    def get_sym_dir_info(self):
        out, err, ret = self.symcfg(['-dir', 'all', '-v', 'list'])
        return out

    def get_sym_dev_info(self):
        out, err, ret = self.symdev(['list'])
        return out

    def get_sym_dev_wwn_info(self):
        out, err, ret = self.symdev(['list', '-wwn'])
        return out

    def get_sym_devrdfa_info(self):
        out, err, ret = self.symdev(['list', '-v', '-rdfa'])
        return out

    def get_sym_ficondev_info(self):
        out, err, ret = self.symdev(['list', '-ficon'])
        return out

    def get_sym_meta_info(self):
        out, err, ret = self.symdev(['list', '-meta', '-v'])
        return out

    def get_sym_dev_name_info(self):
        out, err, ret = self.symdev(['list', '-identifier', 'device_name'])
        return out

    def get_sym_disk_info(self):
        out, err, ret = self.symdisk(['list', '-v'])
        return out

    def get_sym_diskgroup_info(self):
        out, err, ret = self.symdisk(['list', '-dskgrp_summary'])
        return out

    def get_sym_pool_info(self):
        out, err, ret = self.symcfg(['-pool', 'list', '-v'])
        return out

    def get_sym_tdev_info(self):
        out, err, ret = self.symcfg(['list', '-tdev', '-detail'])
        return out

    def get_sym_srp_info(self):
        out, err, ret = self.symcfg(['list', '-srp', '-detail', '-v'])
        return out

    def get_sym_slo_info(self):
        out, err, ret = self.symcfg(['list', '-slo', '-detail', '-v'])
        return out

    def get_sym_sg_info(self):
        out, err, ret = self.symsg(['list', '-v'])
        return out

    def parse_xml(self, buff, key=None, as_list=[], exclude=[]):
        tree = fromstring(buff)
        data = []
        def parse_elem(elem, as_list=[], exclude=[]):
            d = {}
            for e in list(elem):
                if e.tag in exclude:
                    continue
                if e.text.startswith("\n"):
                    child = parse_elem(e, as_list, exclude)
                    if e.tag in as_list:
                        if e.tag not in d:
                            d[e.tag] = []
                        d[e.tag].append(child)
                    else:
                        d[e.tag] = child
                else:
                    if e.tag in as_list:
                        if e.tag not in d:
                            d[e.tag] = []
                        d[e.tag].append(e.text)
                    else:
                        d[e.tag] = e.text
            return d

        for elem in tree.getiterator(key):
            data.append(parse_elem(elem, as_list, exclude))

        return data

    def get_sym_dev_wwn(self, dev):
        out, err, ret = self.symdev(['list', '-devs', dev, '-wwn'])
        return self.parse_xml(out, key="Device")

    def get_sym_dev_show(self, dev):
        out, err, ret = self.symdev(['show', dev])
        return self.parse_xml(out, key="Device")

    def list_pools(self, **kwargs):
        print(json.dumps(self.get_pools(), indent=4))

    def get_pools(self, **kwargs):
        out = self.get_sym_pool_info()
        data = self.parse_xml(out, key="DevicePool")
        return data

    def get_sgs(self, **kwargs):
        out = self.get_sym_sg_info()
        data = self.parse_xml(out, key="SG_Info")
        return data

    def list_sgs(self, **kwargs):
        print(json.dumps(self.get_sgs(), indent=4))

    def load_names(self):
        out = self.get_sym_dev_name_info()
        l = self.parse_xml(out, key="Dev_Info")
        self.dev_names = {}
        for d in l:
            self.dev_names[d["dev_name"]] = d["dev_ident_name"]

    def list_tdevs(self, dev=None, **kwargs):
        self.load_names()
        data = self.get_tdevs(dev)
        for i, d in enumerate(data):
            if d["dev_name"] not in self.dev_names:
                continue
            data[i]["dev_ident_name"] = self.dev_names[d["dev_name"]]
        print(json.dumps(data, indent=4))

    def get_tdevs(self, dev=None, **kwargs):
        if dev:
            out, err, ret = self.symcfg(['list', '-devs', dev, '-tdev', '-detail'])
        else:
            out, err, ret = self.symcfg(['list', '-tdev', '-detail'])
        data = self.parse_xml(out, key="Device", as_list=["pool"])
        return data

    def list_views(self, dev=None, **kwargs):
        if dev is None:
            print(json.dumps(self.get_views(), indent=4))
            return
        views = self.get_dev_views(dev)
        l = []
        for view in views:
            out, err, ret = self.symaccesscmd(["show", "view", view])
            if out.strip() == "":
                continue
            l.append(self.parse_xml(out, key="View_Info", as_list=["Initiators", "Director_Identification", "SG", "Device", "dev_port_info"], exclude=["Initiator_List"]))
        print(json.dumps(l, indent=4))

    def get_views(self, **kwargs):
        out = self.get_sym_view_aclx()
        if out.strip() == "":
            return []
        data = self.parse_xml(out, key="View_Info", as_list=["Initiators", "Director_Identification", "SG", "Device", "dev_port_info"], exclude=["Initiator_List"])
        return data

    def get_dev_views(self, dev):
        sgs = self.get_dev_sgs(dev)
        views = set()
        for sg in sgs:
            out, err, ret = self.symaccesscmd(["show", sg, "-type", "storage"])
            if out.strip() == "":
                continue
            data = self.parse_xml(out, key="Mask_View_Names", as_list=["view_name"])
            for d in data:
                if "view_name" not in d:
                    continue
                views |= set(d["view_name"])
        return views

    def get_initiator_views(self, wwn):
        out, err, ret = self.symaccesscmd(["list", "-type", "initiator", "-wwn", wwn])
        if out.strip() == "":
            return []
        data = self.parse_xml(out, key="Mask_View_Names", as_list=["view_name"])
        views = set()
        for d in data:
            if "view_name" not in d:
                continue
            for view_name in d["view_name"]:
                views.add(view_name.rstrip(" *"))
        return views

    def get_view(self, view):
        out, err, ret = self.symaccesscmd(["show", "view", view])
        data = self.parse_xml(out, key="View_Info", as_list=["Director_Identification", "SG", "Initiator", "Device", "dev_port_info"], exclude=["Initiators"])
        if len(data) == 0:
            return
        return data[0]

    def get_mapping_storage_groups(self, hba_id, tgt_id):
        l = set()
        for view in self.get_initiator_views(hba_id):
            view_data = self.get_view(view)
            if view_data is None:
                continue
            if "port_info" not in view_data:
                continue
            if "Director_Identification" not in view_data["port_info"]:
                continue
            ports = [e["port_wwn"] for e in view_data["port_info"]["Director_Identification"]]
            if tgt_id not in ports:
                continue
            view_sgs = []
            if "SG_Child_info" in view_data and "SG" in view_data["SG_Child_info"] and len(view_data["SG_Child_info"]["SG"]) > 0:
                for sg_data in view_data["SG_Child_info"]["SG"]:
                    view_sgs.append(sg_data["group_name"])
            else:
                view_sgs.append(view_data["stor_grpname"])
            for sg in view_sgs:
                if sg not in self.sg_mappings:
                    self.sg_mappings[sg] = []
                if sg not in self.sg_initiator_count:
                    self.sg_initiator_count[sg] = 0
                self.sg_initiator_count[sg] += len(view_data["Initiator_List"]["Initiator"])
                self.sg_mappings[sg].append({
                    "sg": sg,
                    "view_name": view_data["view_name"],
                    "hba_id": hba_id,
                    "tgt_id": tgt_id,
                })
                l.add(sg)
        return l

    def narrowest_sg(self, sgs):
        if len(sgs) == 0:
            return
        if len(sgs) == 1:
            return sgs[0]
        narrowest = sgs[0]
        for sg in sgs[1:]:
            if self.sg_initiator_count[sg] < self.sg_initiator_count[narrowest]:
                narrowest = sg
        return narrowest

    def translate_mappings(self, mappings):
        sgs = None
        if mappings is None:
            return sgs
        for mapping in mappings:
            elements = mapping.split(":")
            hba_id = elements[0]
            targets = elements[-1].split(",")
            for tgt_id in targets:
                _sgs = self.get_mapping_storage_groups(hba_id, tgt_id)
                if sgs is None:
                    sgs = _sgs
                else:
                    sgs &= _sgs
        return sgs

class Vmax(Sym):
    def __init__(self, sid, symcli_path, symcli_connect, username, password):
        Sym.__init__(self, sid, symcli_path, symcli_connect, username, password)
        self.keys += [
            'sym_ig_aclx',
            'sym_pg_aclx',
            'sym_sg_aclx',
            'sym_view_aclx',
            'sym_pool_info',
            'sym_tdev_info',
            'sym_sg_info',
            'sym_srp_info',
            'sym_slo_info',
        ]
        self.sg_mappings = {}
        self.sg_initiator_count = {}

        if 'SYMCLI_DB_FILE' in os.environ:
            dir = os.path.dirname(os.environ['SYMCLI_DB_FILE'])
            # flat format
            self.aclx = os.path.join(dir, sid+'.aclx')
            if not os.path.exists(self.aclx):
                # emc grab format
                import glob
                files = glob.glob(os.path.join(dir, sid, sid+'*.aclx'))
                if len(files) == 1:
                    self.aclx = files[0]
            if not os.path.exists(self.aclx):
                print("missing file %s"%self.aclx, file=sys.stderr)
        else:
            self.aclx = None

    def symaccesscmd(self, cmd, xml=True, log=False):
        self.set_environ()
        cmd = ['/usr/symcli/bin/symaccess'] + cmd
        if self.aclx is None:
            cmd += ['-sid', self.sid]
        else:
            cmd += ['-file', self.aclx]
        if xml:
            cmd += ['-output', 'xml_element']
        if log and self.node:
            self.log.info(" ".join(cmd))
        return justcall(cmd)

    def get_sym_pg_aclx(self):
        cmd = ['list', '-type', 'port']
        out, err, ret = self.symaccesscmd(cmd)
        return out

    def get_sym_sg_aclx(self):
        cmd = ['list', '-type', 'storage']
        out, err, ret = self.symaccesscmd(cmd)
        return out

    def get_sym_ig_aclx(self):
        cmd = ['list', '-type', 'initiator']
        out, err, ret = self.symaccesscmd(cmd)
        return out

    def get_sym_view_aclx(self):
        cmd = ['list', 'view', '-detail']
        out, err, ret = self.symaccesscmd(cmd)
        return out

    def write_temp_file(self, content):
        import tempfile
        try:
            tmpf = tempfile.NamedTemporaryFile()
            fpath = tmpf.name
            tmpf.close()
            with open(fpath, "w") as tmpf:
                tmpf.write(content)
        except (OSError, IOError) as exc:
            raise ex.excError("failed to write temp file: %s" % str(exc))
        return fpath

    def add_tdev(self, name=None, size=None, srdf=False, rdfg=None, **kwargs):
        """
	     create dev count=<n>,
		  size = <n> [MB | GB | CYL],
		  emulation=<EmulationType>,
		  config=<DevConfig>
		  [, preallocate size = <ALL>
		    [, allocate_type = PERSISTENT]]
		  [, remote_config=<DevConfig>, ra_group=<n>]
		  [, sg=<SgName> [, remote_sg=<SgName>]]
		  [, mapping to dir <director_num:port>
		    [starting] target = <scsi_target>,
		    lun=<scsi_lun>, vbus=<fibre_vbus>
		    [starting] base_address = <cuu_address>[,...]]
		  [, device_attr =
		    <SCSI3_PERSIST_RESERV | DIF1 |
		      AS400_GK>[,...]]
		  [, device_name='<DeviceName>'[,number=<n | SYMDEV> ]];
        """

        if size is None:
            raise ex.excError("The '--size' parameter is mandatory")
        size = convert_size(size, _to="MB")
        _cmd = "create dev count=1, size= %d MB, emulation=FBA, device_attr=SCSI3_PERSIST_RESERV" % size

        if srdf and rdfg:
            _cmd += ", config=RDF1+TDEV, remote_config=RDF2+TDEV, ra_group=%s" % str(rdfg)
        elif srdf and rdfg is None:
            raise ex.excError("--srdf is specified but --rdfg is not")
        else:
            _cmd += ", config=TDEV"

        if name:
            _cmd += ", device_name=%s" % name
        _cmd += ";"
        cmd = ["-cmd", _cmd, "commit", "-noprompt"]
        out, err, ret = self.symconfigure(cmd, xml=False, log=True)
        if ret != 0:
            raise ex.excError(err)
        """
            out contains:
            ...
            New symdev:  003AF [TDEV]
            ...
        """
        for line in out.splitlines():
            line = line.strip()
            if line.startswith("New symdev:"):
                l = line.split()
                if len(l) < 3:
                    raise ex.excError("unable to determine the created SymDevName")
                dev = l[2]
                if srdf:
                    self.set_mode(dev)
                data = self.get_sym_dev_wwn(dev)[0]
                return data
        raise ex.excError("unable to determine the created SymDevName")

    def get_dev_rdf(self, dev):
        data = self.get_sym_dev_show(dev)
        if len(data) != 1:
            raise ex.excError("device %s does not exist" % dev)
        data = data[0]
        if "RDF" not in data or "Local" not in data["RDF"] or "Remote" not in data["RDF"]:
            raise ex.excError("device %s is not handled by srdf" % dev)
        return data["RDF"]

    def write_dev_pairfile(self, dev, rdev):
        content = dev + " " + rdev + "\n"
        self.log.info("write pair file with content: %s", content.strip())
        fpath = self.write_temp_file(content)
        return fpath

    def set_mode(self, dev, **kwargs):
        data = self.get_dev_rdf(dev)
        rdfg = data["Local"]["ra_group_num"]
        rdev = data["Remote"]["dev_name"]
        fpath = self.write_dev_pairfile(dev, rdev)
        cmd = ["-f", fpath, "-rdfg", rdfg, "set", "mode", "sync", "-noprompt"]
        out, err, ret = self.symrdf(cmd, xml=False, log=True)
        os.unlink(fpath)
        if ret != 0:
            raise ex.excError(out+err)

    def createpair(self, pair=None, rdfg=None, srdf_mode=None, srdf_type=None, invalidate=None, **kwargs):
        if pair is None:
            raise ex.excError("the --pair argument is mandatory")
        if srdf_type is None:
            raise ex.excError("the --srdf-type argument is mandatory")
        if srdf_mode is None:
            raise ex.excError("the --srdf-mode argument is mandatory")
        if pair.count(":") != 1:
            raise ex.excError("misformatted pair %s" % pair)
        dev, rdev = pair.split(":")
        try:
            rdf_data = self.get_dev_rdf(dev)
        except ex.excError:
            rdf_data = None
        if rdf_data is not None:
            raise ex.excError("dev %s is already is in a RDF relation" % dev)
        fpath = self.write_dev_pairfile(dev, rdev)
        cmd = ["-f", fpath, "-rdfg", rdfg, "createpair", "-noprompt", "-rdf_mode", srdf_mode, "-type", srdf_type]
        if invalidate in ("R1", "R2"):
            cmd += ["-invalidate", invalidate]
        else:
            cmd += ["-establish"]
        out, err, ret = self.symrdf(cmd, xml=False, log=True)
        if ret != 0:
            os.unlink(fpath)
            raise ex.excError(out+err)
        os.unlink(fpath)

    def deletepair(self, dev=None, **kwargs):
        try:
            data = self.get_dev_rdf(dev)
        except ex.excError:
            return
        rdfg = data["Local"]["ra_group_num"]
        rdev = data["Remote"]["dev_name"]
        fpath = self.write_dev_pairfile(dev, rdev)
        if data["RDF_Info"]["pair_state"] != "Suspended":
            cmd = ["-f", fpath, "-rdfg", rdfg, "suspend", "-noprompt"]
            out, err, ret = self.symrdf(cmd, xml=False, log=True)
            if ret != 0:
                os.unlink(fpath)
                raise ex.excError(out+err)
        cmd = ["-f", fpath, "-rdfg", rdfg, "deletepair", "-noprompt", "-force"]
        out, err, ret = self.symrdf(cmd, xml=False, log=True)
        if ret != 0:
            os.unlink(fpath)
            raise ex.excError(out+err)
        os.unlink(fpath)
        return data

    def rename_disk(self, dev=None, name=None, **kwargs):
        if dev is None:
            raise ex.excError("--dev is mandatory")
        if name is None:
            raise ex.excError("--name is mandatory")
        _cmd = "set dev %s device_name='%s';" % (dev, name)
        cmd = ["-cmd", _cmd, "commit", "-noprompt"]
        out, err, ret = self.symconfigure(cmd, xml=False, log=True)
        if ret != 0:
            raise ex.excError(err)

    def resize_disk(self, dev=None, size=None, force=False, **kwargs):
        if dev is None:
            raise ex.excError("The '--dev' parameter is mandatory")
        if size is None:
            raise ex.excError("The '--size' parameter is mandatory")
        dev_data = self.get_sym_dev_show(dev)
        if len(dev_data) != 1:
            raise ex.excError("device %s does not exist" % dev)
        dev_data = dev_data[0]
        current_size = int(dev_data["Capacity"]["megabytes"])
        if size.startswith("+"):
            incr = convert_size(size.lstrip("+"), _to="MB")
            size = str(current_size + incr)
        else:
            size = str(convert_size(size, _to="MB"))
        if not force and int(size) < current_size:
            raise ex.excError("the target size is smaller than the current "
                              "size. refuse to process. use --force if you "
                              "accept the data loss risk.")
        if "RDF" in dev_data:
            rdf_data = dev_data["RDF"]
        else:
            rdf_data = None
        if rdf_data:
            self.deletepair(dev)
        cmd = ["modify", dev, "-tdev", "-cap", str(size), "-captype", "mb", "-noprompt"]
        out, err, ret = self.symdev(cmd, xml=False, log=True)
        if ret != 0:
            raise ex.excError(err)
        results = {
            "driver_data": {
            },
        }
        if rdf_data:
            results["driver_data"]["rdf"] = rdf_data
        return results

    def del_tdev(self, dev=None, **kwargs):
        if dev is None:
            raise ex.excError("The '--dev' parameter is mandatory")
        data = self.get_sym_dev_wwn(dev)
        if len(data) == 0:
            self.log.info("%s does not exist", dev)
            return
        data = data[0]
        cmd = ["delete", dev, "-noprompt"]
        out, err, ret = self.symdev(cmd, xml=False, log=True)
        if ret != 0:
            raise ex.excError(err)
        self.del_diskinfo(data["wwn"])

    def del_disk(self, dev=None, **kwargs):
        try:
            rdf_data = self.get_dev_rdf(dev)
        except ex.excError as exc:
            self.log.info("rdf data: %s", exc)
            rdf_data = None
        self.set_dev_ro(dev)
        self.del_map(dev)
        self.deletepair(dev)
        retry = 5
        while retry > 0:
            self.free_tdev(dev)
            try:
                self.del_tdev(dev=dev, **kwargs)
                break
            except ex.excError as exc:
                if "A free of all allocations is required" in str(exc):
                    if retry == 1:
                        raise ex.excError("dev %s is still not free of all allocations after 5 tries")
                    # retry
                    retry -= 1
                    time.sleep(5)
                    continue
                raise
        results = {
            "driver_data": {
            },
        }
        if rdf_data:
            results["driver_data"]["rdf"] = rdf_data
        return results

    def del_map(self, dev, **kwargs):
        for sg in self.get_dev_sgs(dev):
            self.del_tdev_from_sg(dev, sg)

    def free_tdev(self, dev):
        while True:
            out, err, ret = self.symdev(["free", "-devs", dev, "-all", "-noprompt"], xml=False, log=True)
            if self.tdev_freed(dev) and not self.tdev_deallocating(dev) and not self.tdev_freeingall(dev):
                break
            time.sleep(5)

    def set_dev_ro(self, dev):
        out, err, ret = self.symdev(["write_disable", dev, "-noprompt"], xml=False, log=True)
        return out, err, ret

    def tdev_freed(self, dev):
        out, err, ret = self.symcfg(["list", "-tdevs", "-devs", dev], xml=True)
        data = self.parse_xml(out, key="Device")
        if len(data) == 0:
            return True
        data = data[0]
        self.log.info("device %s has %s tracks allocated", dev, str(data["alloc_tracks"]))
        if data["alloc_tracks"] in ("0", 0):
            return True
        return False

    def tdev_freeingall(self, dev):
        return self.tdev_status(dev, "-freeingall")

    def tdev_deallocating(self, dev):
        return self.tdev_status(dev, "-deallocating")

    def tdev_status(self, dev, status):
        out, err, ret = self.symcfg(["verify", "-tdevs", "-devs", dev, status], xml=False)
        outv = out.strip().split()
        if len(outv) == 0:
            raise ex.excError("unexpected verify output: %s" % out+err)
        if outv[0] == "None":
            self.log.info("device %s is not %s", dev, status)
            return False
        self.log.info("device %s is %s", dev, status)
        return True

    def add_tdev_to_sg(self, dev, sg):
        if sg is None:
            return
        cmd = ["-name", sg, "-type", "storage", "add", "dev", dev]
        out, err, ret = self.symaccesscmd(cmd, xml=False, log=True)
        if ret != 0:
            self.log.error(err)
        return out, err, ret

    def del_tdev_from_sg(self, dev, sg):
        cmd = ["-name", sg, "-type", "storage", "remove", "dev", dev, "-unmap"]
        out, err, ret = self.symaccesscmd(cmd, xml=False, log=True)
        if ret != 0:
            self.log.error(err)
        return out, err, ret

    def get_dev_sgs(self, dev):
        out, err, ret = self.symaccesscmd(["list", "-type", "storage", "-devs", dev])
        data = self.parse_xml(out, key="Group_Info")
        return [d["group_name"] for d in data if d["Status"] != "IsParent"]

    def get_sg(self, sg):
        out, err, ret = self.symsg(["show", sg])
        data = self.parse_xml(out, key="SG_Info")
        return data[0]

    def filter_sgs(self, sgs, srp=None, slo=None):
        filtered_sgs = []
        if srp is None and slo is None:
            return sgs
        for sg in sgs:
            data = self.get_sg(sg)
            if srp and data["SRP_name"] != srp:
                self.log.info("discard sg %s (srp %s, required %s)", sg, data["SRP_name"], srp)
                continue
            if slo and data["SLO_name"] != slo:
                self.log.info("discard sg %s (slo %s, required %s)", sg, data["SLO_name"], slo)
                continue
            filtered_sgs.append(sg)
        return filtered_sgs

    def get_lun(self, dev, hba_id, tgt_id, view_name):
        view = self.get_view(view_name)
        if view is None:
            return
        port = None
        for port in view["port_info"]["Director_Identification"]:
            if port["port_wwn"] == tgt_id:
                port_id = port["port"]
                break
        if port is None:
            return
        if "Device" not in view:
            return
        for device in view["Device"]:
            if device["dev_name"] != dev:
                continue
            for port in device["dev_port_info"]:
                if port_id == port["port"]:
                    return port["host_lun"]

    def get_mappings(self, dev):
        mappings = {}
        for sg in self.get_dev_sgs(dev):
            for sg, l in self.sg_mappings.items():
                for d in l:
                    d["lun"] = self.get_lun(dev, d["hba_id"], d["tgt_id"], d["view_name"])
                    if d["lun"] is None:
                        continue
                    mappings[d["hba_id"] + ":" + d["tgt_id"]] = d
        return mappings

    def add_disk(self, name=None, size=None, slo=None, srp=None, srdf=False, rdfg=None, mappings=None, **kwargs):
        sg = self.mappings_to_sg(mappings, slo, srp)
        dev_data = self.add_tdev(name, size, srdf, rdfg, **kwargs)
        self._add_map(dev_data["dev_name"], mappings, slo, srp, sg)
        self.push_diskinfo(dev_data, name, size, srp, sg)
        mappings = {}
        results = {
            "disk_id": dev_data["wwn"],
            "disk_devid": dev_data["dev_name"],
            "mappings": self.get_mappings(dev_data["dev_name"]),
            "driver_data": {
                "dev": dev_data,
            },
        }
        return results

    def _add_map(self, dev=None, mappings=None, slo=None, srp=None, sg=None, **kwargs):
        if dev is None:
            raise ex.excError("--dev is mandatory")
        if sg is None:
            sg = self.mappings_to_sg(mappings, slo, srp)
        self.add_tdev_to_sg(dev, sg)

    def add_map(self, dev=None, mappings=None, slo=None, srp=None, sg=None, **kwargs):
        self._add_map(dev, mappings, slo, srp, sg)
        dev_data = self.get_sym_dev_wwn(dev)[0]
        results = {
            "disk_id": dev_data["wwn"],
            "disk_devid": dev_data["dev_name"],
            "mappings": self.get_mappings(dev_data["dev_name"]),
            "driver_data": {
                "dev": dev_data,
            },
        }
        return results

    def mappings_to_sg(self, mappings, slo, srp):
        if mappings is None:
            return
        sgs = self.translate_mappings(mappings)
        if len(sgs) == 0:
            raise ex.excError("no storage group found for the requested mappings")
        sgs = self.filter_sgs(sgs, srp=srp, slo=slo)
        if len(sgs) == 0:
            raise ex.excError("no storage group found for the requested mappings")
        narrowest = self.narrowest_sg(sgs)
        self.log.info("candidates sgs: %s, retain: %s", str(sgs), narrowest)
        return narrowest

    def del_diskinfo(self, disk_id):
        if disk_id in (None, ""):
            return
        if self.node is None:
            return
        try:
            ret = self.node.collector_rest_delete("/disks/%s" % disk_id)
        except Exception as exc:
            raise ex.excError(str(exc))
        if "error" in ret:
            self.log.error("failed to delete the disk object in the collector: %s", ret["error"])
        return ret

    def push_diskinfo(self, data, name, size, srp, sg):
        if self.node is None:
            return
        try:
            ret = self.node.collector_rest_post("/disks", {
                "disk_id": data["wwn"],
                "disk_devid": data["dev_name"],
                "disk_name": name if name else "",
                "disk_size": convert_size(size, _to="MB"),
                "disk_alloc": 0,
                "disk_arrayid": self.sid,
                "disk_group": srp,
            })
        except Exception as exc:
            raise ex.excError(str(exc))
        if "error" in data:
            raise ex.excError(ret["error"])
        return ret


class Dmx(Sym):
    def __init__(self, sid):
        Sym.__init__(self, sid)
        self.keys += ['sym_maskdb']

        if 'SYMCLI_DB_FILE' in os.environ:
            dir = os.path.dirname(os.environ['SYMCLI_DB_FILE'])
            # flat format
            self.maskdb = os.path.join(dir, sid+'.bin')
            if not os.path.exists(self.maskdb):
                # emc grab format
                self.maskdb = os.path.join(dir, sid, 'symmaskdb_backup.bin')
            if not os.path.exists(self.maskdb):
                print("missing file %s"%self.maskdb, file=sys.stderr)
        else:
            self.maskdb = None

    def symaccesscmd(self, cmd, xml=True, log=False):
        self.set_environ()
        cmd = ['/usr/symcli/bin/symaccess'] + cmd
        if self.maskdb is None:
            cmd += ['-sid', self.sid]
        else:
            cmd += ['-f', self.maskdb]
        if xml:
            cmd += ['-output', 'xml_element']
        if log and self.node:
            self.log.info(" ".join(cmd))
        return justcall(cmd)

    def get_sym_maskdb(self):
        cmd = ['list', 'database']
        out, err, ret = self.symaccesscmd(cmd)
        return out



def do_action(action, array_name=None, node=None, **kwargs):
    o = Arrays()
    array = o.get_array(array_name)
    if array is None:
        raise ex.excError("array %s not found" % array_name)
    if not hasattr(array, action):
        raise ex.excError("not implemented")
    array.node = node
    node.log.handlers[1].setLevel(logging.CRITICAL)
    ret = getattr(array, action)(**kwargs)
    if ret is not None:
        print(json.dumps(ret, indent=4))

def main(argv, node=None):
    parser = OptParser(prog=PROG, options=OPT, actions=ACTIONS,
                       deprecated_actions=DEPRECATED_ACTIONS,
                       global_options=GLOBAL_OPTS)
    options, action = parser.parse_args(argv)
    kwargs = vars(options)
    do_action(action, node=node, **kwargs)

if __name__ == "__main__":
    try:
        ret = main(sys.argv)
    except ex.excError as exc:
        print(exc, file=sys.stderr)
        ret = 1
    sys.exit(ret)

   0707010005c999000081b40000000000000000000000015ab21754000019c6000000660000000000000000000000000000002400000000root/usr/share/opensvc/lib/rcZfs.py   from rcUtilities import justcall, call, vcall
from rcGlobalEnv import rcEnv
import rcExceptions as ex
import logging
import sys
"""
"""

def dataset_exists(device, dstype):
    """
    Return True if the data exists.
    """
    return Dataset(device).exists(dstype)

def zfs_getprop(dataset='undef_ds', propname='undef_prop'):
    """
    Return the dataset property <propname> value
    """
    cmd = [rcEnv.syspaths.zfs, 'get', '-Hp', '-o', 'value', propname, dataset]
    (stdout, stderr, retcode) = justcall(cmd)
    if retcode == 0 :
        return stdout.split("\n")[0]
    else:
        return ""

def zfs_setprop(dataset='undef_ds', propname='undef_prop', propval='undef_val'):
    """
    Set the dataset property <propname> to value <propval>.
    """
    if zfs_getprop(dataset, propname) == propval :
        return True
    cmd = [rcEnv.syspaths.zfs, 'set', propname + '='+ propval, dataset]
    print(' '.join(cmd))
    (retcode, stdout, stderr) = vcall(cmd)
    if retcode == 0 :
        return True
    else:
        return False

def a2pool_dataset(s):
    """
    Return the (pool, dataset) tuple from a mount point.

    Examples:
    * / => ('rpool','rpool/ROOT/opensolaris-b134')
    * rpool/ROOT/opensolaris-b134 => ('rpool','rpool/ROOT/opensolaris-b134')
    """
    if len(s) == 0:
        return ("", "")
    ss = s
    if s[0] == '/':
        cmd = [rcEnv.syspaths.zfs, 'list', '-H',  '-o', 'name', s]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return ("", "")
        ss = out.split('\n')[0]
    x = ss.split('/')
    if len(x) < 2:
        return (ss, ss)
    return (x[0], ss)

class Dataset(object):
    """
    A class exposing usual ops on a zfs dataset.
    """
    log = None
    def __init__(self, name, log=None):
        self.name = name
        if log is None:
            if Dataset.log is None:
                Dataset.log = logging.getLogger("DATASET".upper())
                Dataset.log.addHandler(logging.StreamHandler(sys.stdout))
                Dataset.log.setLevel(logging.INFO)
            self.log = Dataset.log
        else:
            self.log = log
    def __str__(self, option=None):
        if option is None:
            cmd = [rcEnv.syspaths.zfs, 'list', self.name]
        else:
            cmd = [rcEnv.syspaths.zfs, 'list'] + option + [self.name]
        (retcode, stdout, stderr) = call(cmd, log=self.log)
        if retcode == 0:
            return stdout
        else:
            return "Failed to list info for dataset: %s" % (self.name)

    def exists(self, type="all"):
        """
        Return True if the dataset exists else return False.
        If type is set, also verify the dataset type.
        """
        out, err, ret = justcall(["zfs", "get", "-H", "-o", "value", "type", self.name])
        if ret == 0 and type == "all":
            return True
        elif ret == 0 and out.split('\n')[0] == type:
            return True
        else:
            return False

    def rename(self, name=None, option=None):
        """
        Rename the dataset.
        """
        if option is None:
            cmd = [rcEnv.syspaths.zfs, 'rename', self.name, name]
        else:
            cmd = [rcEnv.syspaths.zfs, 'rename'] + option + [self.name, name]
        ret, _, _ = vcall(cmd, log=self.log)
        if ret == 0:
            return True
        else:
            return False

    def create(self, option=None):
        """
        Create the dataset with options.
        """
        if option is None:
            cmd = [rcEnv.syspaths.zfs, 'create', self.name]
        else:
            cmd = [rcEnv.syspaths.zfs, 'create'] + option + [self.name]
        ret, _, _ = vcall(cmd, log=self.log)
        if ret == 0:
            return True
        else:
            return False

    def destroy(self, options=[]):
        """
        Destroy the dataset.
        """
        if not self.exists():
            return True
        cmd = [rcEnv.syspaths.zfs, 'destroy'] + options + [self.name]
        ret, _, _ = vcall(cmd, log=self.log)
        if ret == 0:
            return True
        else:
            return False

    def getprop(self, propname):
        """
        Return a dataset propertie value or '' on error.
        """
        cmd = [rcEnv.syspaths.zfs, 'get', '-Hp', '-o', 'value', propname, self.name]
        out, _, ret = justcall(cmd)
        if ret == 0 :
            return out.rstrip('\n')
        else:
            return ""

    def setprop(self, propname, propval, err_to_warn=False, err_to_info=False):
        """
        Set Dataset property value.
        Return True is success else return False.
        """
        cmd = [rcEnv.syspaths.zfs, 'set', propname + '='+ propval, self.name]
        ret, out, err = vcall(cmd, log=self.log,
                              err_to_warn=err_to_warn,
                              err_to_info=err_to_info)
        if ret == 0 :
            return True
        else:
            return False

    def verify_prop(self, nv_pairs={}, err_to_warn=False, err_to_info=False):
        """
        For name, val from nv_pairs dict,
        if zfs name property value of dataset differ from val
        then zfs set name=value on dataset object.
        """
        for name in nv_pairs.keys():
            if self.getprop(name) != nv_pairs[name]:
                self.setprop(propname=name, propval=nv_pairs[name],
                            err_to_warn=err_to_warn,
                            err_to_info=err_to_info)

    def snapshot(self, snapname=None, recursive=False):
        """
        Snapshot the dataset.
        Return the snapshot dataset object.
        Return False on error.
        """
        if snapname is None:
            raise ex.excError("snapname should be defined")
        snapdataset = self.name + "@" + snapname
        cmd = [rcEnv.syspaths.zfs, 'snapshot']
        if recursive:
            cmd.append("-r")
        cmd.append(snapdataset)
        ret, _, _ = vcall(cmd, log=self.log)
        if ret == 0:
            return Dataset(snapdataset)
        else:
            return False

    def clone(self, name, option=None):
        """
        Clone the dataset with options
        Return the clone dataset object.
        Return False on failure.
        """
        if option is None:
            cmd = [rcEnv.syspaths.zfs, 'clone', self.name, name]
        else:
            cmd = [rcEnv.syspaths.zfs, 'clone'] + option + [self.name, name]
        ret, _, _ = vcall(cmd, log=self.log)
        if ret == 0:
            return Dataset(name)
        else:
            return False

  0707010005c97e000081b40000000000000000000000015ab2175400003548000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/rcSysReport.py from __future__ import print_function
import os
import sys
import shutil
import glob
import json
from stat import *
from subprocess import *

import rcExceptions as ex
from rcGlobalEnv import rcEnv
from rcUtilities import which, cmdline2list

class SysReport(object):
    def __init__(self, node=None):
        self.todo = [
          ('INC', os.path.join(rcEnv.paths.pathetc, 'node.conf')),
          ('INC', os.path.join(rcEnv.paths.pathetc, '*.conf')),
          ('INC', os.path.join(rcEnv.paths.pathetc, 'sysreport.conf.d')),
        ]

        self.changed = []
        self.deleted = []
        self.sysreport_conf_d = os.path.join(rcEnv.paths.pathetc, "sysreport.conf.d")
        self.sysreport_d = os.path.join(rcEnv.paths.pathvar, "sysreport")
        self.collect_d = os.path.join(self.sysreport_d, rcEnv.nodename)
        self.collect_cmd_d = os.path.join(self.collect_d, "cmd")
        self.collect_file_d = os.path.join(self.collect_d, "file")
        self.collect_stat = os.path.join(self.collect_file_d, "stat")
        self.full = [self.collect_stat]
        self.stat_changed = False
        self.root_uid = 0
        self.root_gid = 0
        self.node = node
        self.archive_extension = '.tar'
        self.send_rpc = "send_sysreport"
        self.lstree_rpc = "sysreport_lstree"

    def init(self):
        self.init_dir(self.collect_d)
        self.init_dir(self.collect_cmd_d)
        self.init_dir(self.collect_file_d)
        self.init_dir(self.sysreport_conf_d)
        self.load_stat()
        self.merge_todo()

    def init_dir(self, fpath):
        self.init_collect_d(fpath)
        self.init_collect_d_ownership(fpath)
        self.init_collect_d_perms(fpath)

    def init_collect_d(self, fpath):
        if not os.path.exists(fpath):
            print("create dir", fpath)
            os.makedirs(fpath)

    def init_collect_d_perms(self, fpath):
        s = os.stat(fpath)
        mode = s[ST_MODE]
        if mode != 16768:
            print("set dir", fpath, "mode to 0600")
            os.chmod(fpath, 0o0600)

    def init_collect_d_ownership(self, fpath):
        s = os.stat(fpath)
        if s.st_uid != self.root_uid or s.st_gid != self.root_gid:
            print("set dir", self.collect_d, "ownership to", self.root_uid, self.root_gid)
            os.chown(self.collect_d, self.root_uid, self.root_gid)

    def load_stat(self):
        try:
            self.stat = self._load_stat()
        except:
            self.stat = {}

    def _load_stat(self):
        with open(self.collect_stat, "r") as f:
            buff = f.read()
        l = json.loads(buff)
        stat = {}
        for e in l:
            try:
                stat[e["fpath"]] = e
            except:
                pass
        return stat

    def write_stat(self):
        if not self.stat_changed:
            return
        self._write_stat()

    def _write_stat(self):
        l = []
        for fpath in sorted(self.stat.keys()):
            l.append(self.stat[fpath])
        with open(self.collect_stat, "w") as f:
            f.write(json.dumps(l, sort_keys=True, separators=[", ", ": "], indent=4))

    def merge_todo(self):
        for root, dnames, fnames in os.walk(self.sysreport_conf_d):
            for fname in fnames:
                fpath = os.path.join(self.sysreport_conf_d, fname)
                s = os.stat(fpath)
                mode = s[ST_MODE]
                if mode & S_IWOTH:
                    print("skip %s config file: file mode is insecure ('other' has write permission)" % fpath)
                    continue
                if s.st_uid != self.root_uid or s.st_gid != self.root_gid:
                    print("skip %s config file: file ownership is insecure (must be owned by root)" % fpath)
                    continue

                with open(fpath, 'r') as f:
                    buff = f.read()
                for line in buff.split("\n"):
                    line = line.strip()
                    if line.startswith("FILE"):
                        t = ("INC", line[4:].strip())
                    elif line.startswith("CMD"):
                        t = ("CMD", line[3:].strip())
                    elif line.startswith("DIR"):
                        t = ("INC", line[3:].strip())
                    elif line.startswith("GLOB"):
                        t = ("INC", line[4:].strip())
                    elif line.startswith("INC"):
                        t = ("INC", line[3:].strip())
                    elif line.startswith("EXC"):
                        t = ("EXC", line[3:].strip())
                    elif line == "":
                        continue
                    elif line.startswith("#"):
                        continue
                    elif line.startswith(";"):
                        continue
                    else:
                        print("unsupported item type:", line)
                        continue
                    if t not in self.todo:
                        self.todo.append(t)

        # expand
        inc = set()
        exc = set()
        self.cmds = set()
        for mode, s in self.todo:
            if mode == "CMD":
                self.cmds.add(s)
                continue
            l = []
            for _s in glob.glob(s):
                if os.path.isdir(_s):
                    l += self.find_files(_s)
                else:
                    l.append(_s)
            if mode == "INC":
                inc |= set(l)
            elif mode == "EXC":
                exc |= set(l)
        self.files = inc - exc

        # find deleted
        dst_files = self.find_files(self.collect_file_d)
        n = len(self.collect_file_d)
        dst_files = map(lambda x: x[n:], dst_files)
        self.deleted = set(dst_files) - self.files - set(["/stat"])

        # order file lists
        self.files = sorted(list(self.files))
        self.deleted = sorted(list(self.deleted))

        # purge stat info of deleted files
        for fpath in self.deleted:
            if fpath in self.stat:
                del(self.stat[fpath])
                self.stat_changed = True
                if self.collect_stat not in self.changed:
                    self.changed.append(self.collect_stat)
                if self.collect_stat not in self.full:
                    self.full.append(self.collect_stat)

    def cmdlist2fname(self, l):
        fname = '(space)'.join(l)
        fname = fname.replace('|','(pipe)')
        fname = fname.replace('&','(amp)')
        fname = fname.replace('$','(dollar)')
        fname = fname.replace('^','(caret)')
        fname = fname.replace('/','(slash)')
        fname = fname.replace(':','(colon)')
        fname = fname.replace(';','(semicolon)')
        fname = fname.replace('<','(lt)')
        fname = fname.replace('>','(gt)')
        fname = fname.replace('=','(eq)')
        fname = fname.replace('?','(question)')
        fname = fname.replace('@','(at)')
        fname = fname.replace('!','(excl)')
        fname = fname.replace('#','(num)')
        fname = fname.replace('%','(pct)')
        fname = fname.replace('"','(dquote)')
        fname = fname.replace("'",'(squote)')
        return fname

    def write(self, fpath, buff):
        try:
            with open(fpath, 'r') as f:
                pbuff = f.read()
            if buff != pbuff:
                self.changed.append(fpath)
        except IOError:
            self.changed.append(fpath)
        with open(fpath, 'w') as f:
            f.write(buff)
        self.full.append(fpath)

    def collect_cmd(self, cmd):
        l = cmdline2list(cmd)
        if len(l) == 0:
            print(" err: syntax error")
            return
        if not os.path.exists(l[0]):
            return
        if which(l[0]) is None:
            print(" err: not executable")
            return
        fname = self.cmdlist2fname(l)
        cmd_d = os.path.join(self.collect_cmd_d, fname)
        p = Popen(l, stdout=PIPE, stderr=STDOUT, close_fds=True)
        out, err = p.communicate()
        if sys.version_info[0] >= 3:
            out = out.decode("utf-8")
        self.write(os.path.join(cmd_d), out)

    def get_stat(self, fpath):
        st = os.stat(fpath)
        stat = {
          "fpath": fpath,
          "realpath": os.path.realpath(fpath),
          "mode": oct(st[ST_MODE]),
          "uid": st[ST_UID],
          "gid": st[ST_GID],
          "dev": st[ST_DEV],
          "nlink": st[ST_NLINK],
          "mtime": st[ST_MTIME],
          "ctime": st[ST_CTIME],
        }
        return stat

    def push_stat(self, fpath):
        stat = self.get_stat(fpath)
        cached_stat = self.stat.get(fpath)
        if cached_stat is None:
            self.stat[fpath] = stat
            self.stat_changed = True
            if self.collect_stat not in self.changed:
                self.changed.append(self.collect_stat)
            if self.collect_stat not in self.full:
                self.full.append(self.collect_stat)
            #print("  add %s stat info"%fpath)
            return
        for p in ("realpath", "mode", "uid", "gid", "dev", "nlink", "mtime", "ctime"):
            if stat[p] != cached_stat[p]:
                self.stat[fpath] = stat
                self.stat_changed = True
                if self.collect_stat not in self.changed:
                    self.changed.append(self.collect_stat)
                if self.collect_stat not in self.full:
                    self.full.append(self.collect_stat)
                #print("  change %s stat info"%fpath)
                return

    def collect_file(self, fpath):
        if not os.path.exists(fpath):
            return
        if os.path.islink(fpath):
            return
        dst_d = self.collect_file_d + os.path.dirname(fpath)
        fname = os.path.basename(fpath)
        dst_f = os.path.join(dst_d, fname)
        if os.path.isdir(dst_f):
            # the fpath has changed from dir to file. cleanup the dst tree.
            shutil.rmtree(dst_f)
        if not os.path.exists(dst_d):
            os.makedirs(dst_d)

        self.push_stat(fpath)

        try:
            with open(fpath, 'r') as f:
                buff = f.read()
            with open(dst_f, 'r') as f:
                pbuff = f.read()
            if buff != pbuff:
                self.changed.append(dst_f)
        except IOError:
            # in doubt, send ... git will know better on the collector
            self.changed.append(dst_f)
        except UnicodeDecodeError:
            # binary file: skip
            pass
        shutil.copy2(fpath, dst_f)
        self.full.append(dst_f)

    def delete_collected(self, fpaths):
        for fpath in fpaths:
            self.delete_collected_one(fpath)

    def delete_collected_one(self, fpath):
        fp = self.collect_file_d + fpath
        os.unlink(fp)

    def find_files(self, fpath):
        l = []
        if not os.path.exists(fpath):
            return l
        for item in os.listdir(fpath):
            _fpath = os.path.join(fpath, item)
            if os.path.isdir(_fpath):
                l += self.find_files(_fpath)
            elif not os.path.islink(_fpath):
                l.append(_fpath)
        return l

    def sysreport(self, force=False):
        self.node.collector.init(self.send_rpc)
        if self.node.collector.proxy is None:
            print("no collector connexion. abort sysreport")
            return 1
        self.init()
        print("collect directory is", self.collect_d)
        for fpath in self.files:
            self.collect_file(fpath)
        for cmd in self.cmds:
            self.collect_cmd(cmd)
        self.delete_collected(self.deleted)
        self.write_stat()
        self.send(force)

    def deleted_report(self):
        print("files deleted:")
        for fpath in sorted(self.deleted):
            print("  "+fpath)

    def send(self, force=False):
        if force:
            to_send = self.full
            lstree_data = self.node.collector.call(self.lstree_rpc)
            if lstree_data is None:
                raise ex.excError("can not get lstree from collector")
            n = len(self.collect_d)+1
            self.deleted = sorted(list(set(lstree_data) - set("file/stat") - set(map(lambda x: x[n:], self.full))))
        else:
            to_send = self.changed
            self.changed_report()

        if len(self.deleted) > 0:
            self.deleted_report()

        if len(to_send) == 0 and len(self.deleted) == 0:
            print("no change to report")
            return

        if len(to_send) > 0:
            tmpf = self.archive(to_send)
        else:
            tmpf = None

        print("sending sysreport")
        self.node.collector.call(self.send_rpc, tmpf, self.deleted)

        if tmpf is not None:
            os.unlink(tmpf)

    def archive(self, l):
        import tarfile
        import tempfile
        f = tempfile.NamedTemporaryFile(prefix="sysreport.", suffix=self.archive_extension, dir=self.collect_d)
        tmpf = f.name
        f.close()
        cwd = os.getcwd()
        os.chdir(self.sysreport_d)
        n = len(self.sysreport_d) + 1
        print("creating tarball", tmpf)
        tar = tarfile.open(tmpf, mode="w")
        for fpath in l:
            if len(fpath) < n:
                print(" err: can not archive", fpath, "(fpath too short)")
                continue
            tar.add(fpath[n:])
        tar.close()
        os.chdir(cwd)
        return tmpf

    def changed_report(self):
        if len(self.changed) > 0:
            print("changed files:")
            for fpath in self.changed:
                print(" ", fpath)

0707010005c97f000081b40000000000000000000000015ab2175400000098000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/rcSysReportAIX.py  import rcSysReport

class SysReport(rcSysReport.SysReport):
    def __init__(self, node=None):
        rcSysReport.SysReport.__init__(self, node=node)

0707010005c980000081b40000000000000000000000015ab2175400000098000000660000000000000000000000000000003000000000root/usr/share/opensvc/lib/rcSysReportDarwin.py   import rcSysReport

class SysReport(rcSysReport.SysReport):
    def __init__(self, node=None):
        rcSysReport.SysReport.__init__(self, node=node)

0707010005c981000081b40000000000000000000000015ab2175400000098000000660000000000000000000000000000003100000000root/usr/share/opensvc/lib/rcSysReportFreeBSD.py  import rcSysReport

class SysReport(rcSysReport.SysReport):
    def __init__(self, node=None):
        rcSysReport.SysReport.__init__(self, node=node)

0707010005c982000081b40000000000000000000000015ab2175400000098000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/rcSysReportHP-UX.py    import rcSysReport

class SysReport(rcSysReport.SysReport):
    def __init__(self, node=None):
        rcSysReport.SysReport.__init__(self, node=node)

0707010005c983000081b40000000000000000000000015ab2175400000098000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/rcSysReportLinux.py    import rcSysReport

class SysReport(rcSysReport.SysReport):
    def __init__(self, node=None):
        rcSysReport.SysReport.__init__(self, node=node)

0707010005c984000081b40000000000000000000000015ab2175400000098000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/rcSysReportOSF1.py import rcSysReport

class SysReport(rcSysReport.SysReport):
    def __init__(self, node=None):
        rcSysReport.SysReport.__init__(self, node=node)

0707010005c985000081b40000000000000000000000015ab2175400000098000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/rcSysReportSunOS.py    import rcSysReport

class SysReport(rcSysReport.SysReport):
    def __init__(self, node=None):
        rcSysReport.SysReport.__init__(self, node=node)

0707010005c986000081b40000000000000000000000015ab21754000000df000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/rcUpdatePkgAIX.py  from subprocess import *

repo_subdir = "rpms"

def update(fpath):
    cmd = ['rpm', '-U', fpath, '--force', '--ignoreos', '--nodeps']
    print(' '.join(cmd))
    p = Popen(cmd)
    p.communicate()
    return p.returncode
 0707010005c987000081b40000000000000000000000015ab21754000001c0000000660000000000000000000000000000003000000000root/usr/share/opensvc/lib/rcUpdatePkgDarwin.py   from subprocess import *
import os

repo_subdir = "macos-pkg"

def update(fpath):
    # macos installer expect a .pkg file extension
    pkgfile = fpath+'.pkg'
    print("renaming %s to %s"%(fpath, pkgfile))
    os.rename(fpath, pkgfile)
    cmd = ['installer', '-package', pkgfile, '-target', '/']
    print(' '.join(cmd))
    p = Popen(cmd)
    p.communicate()
    try:
        os.unlink(pkgfile)
    except:
        pass
    return p.returncode
0707010005c988000081b40000000000000000000000015ab2175400000108000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/rcUpdatePkgHP-UX.py    from subprocess import *

repo_subdir = "depot"

def update(fpath):
    cmd = ['swinstall', '-x', 'mount_all_filesystems=false', '-x', 'allow_downdate=true', '-s', fpath, '*']
    print(' '.join(cmd))
    p = Popen(cmd)
    p.communicate()
    return p.returncode
0707010005c989000081b40000000000000000000000015ab2175400000242000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/rcUpdatePkgLinux.py    from subprocess import *
import os


def update_deb(fpath):
    cmd = ['dpkg', '-i', fpath]
    print(' '.join(cmd))
    p = Popen(cmd)
    p.communicate()
    return p.returncode

def update_rpm(fpath):
    cmd = ['rpm', '-U', fpath, '--force', '--nodeps']
    print(' '.join(cmd))
    p = Popen(cmd)
    p.communicate()
    return p.returncode

if os.path.exists('/etc/debian_version'):
    update = update_deb
    repo_subdir = "deb"
elif os.path.exists('/etc/SuSE-release') or \
     os.path.exists('/etc/redhat-release'):
    repo_subdir = "rpms"
    update = update_rpm


  0707010005c98a000081b40000000000000000000000015ab21754000002ad000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/rcUpdatePkgOSF1.py from __future__ import print_function
import os
import sys
import tarfile
from rcGlobalEnv import rcEnv

repo_subdir = "tar"

def update(fpath):
    cmd = sys.executable + ' ' + rcEnv.paths.preinstall
    ret = os.system(cmd)
    if ret != 0:
        return

    oldpath = os.getcwd()
    os.chdir("/")
    tar = tarfile.open(fpath)
    try:
        tar.extractall()
        tar.close()
    except:
        try:
            os.unlink(fpath)
        except:
            pass
        print("failed to unpack", file=sys.stderr)
        return 1
    try:
        os.unlink(fpath)
    except:
        pass

    cmd = sys.executable + ' ' + rcEnv.paths.postinstall
    return os.system(cmd)
   0707010005c98b000081b40000000000000000000000015ab217540000065a000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/rcUpdatePkgSunOS.py    from subprocess import *
from rcUtilitiesSunOS import get_solaris_version
import os

repo_subdir = "sunos-pkg"

def gen_adminfile():
    filename = "/var/tmp/opensvc.adminfile"
    f = open (filename, 'w')
    f.write("mail=\n")
    f.write("instance=overwrite\n")
    f.write("partial=nocheck\n")
    f.write("runlevel=nocheck\n")
    f.write("idepend=nocheck\n")
    f.write("rdepend=nocheck\n")
    f.write("space=nocheck\n")
    f.write("setuid=nocheck\n")
    f.write("conflict=nocheck\n")
    f.write("action=nocheck\n")
    f.write("basedir=default\n")
    f.close()
    return filename

def update(fpath):
    # check downloaded package integrity
    cmd = ['pkgchk', '-d', fpath, 'all']
    print(' '.join(cmd))
    p = Popen(cmd)
    p.communicate()
    if p.returncode != 0:
        return 1

    # check if initially installed with pkgadd -G
    file = '/var/sadm/install/gz-only-packages'
    GlobalOnly = False
    if os.path.isfile(file):
        f = open(file)
        for line in f:
            if line.startswith("opensvc"):
                print("OpenSVC package was previously installed with pkgadd -G\n")
                GlobalOnly = True

    cmd = ['pkgrm', '-n', 'opensvc']
    print(' '.join(cmd))
    p = Popen(cmd)
    p.communicate()
    if p.returncode != 0:
        return 1
    osver = get_solaris_version()
    if osver < 10.0:
        opts = ''
    else:
        if GlobalOnly is True:
            opts = '-G'
        else:
            opts = ''
    admin = gen_adminfile()
    opts += " -a %s " % admin
    cmd = 'pkgadd %s -d %s all' % (opts, fpath)
    print(cmd)
    return os.system(cmd)
  0707010005c98c000081b40000000000000000000000015ab21754000000b2000000660000000000000000000000000000003100000000root/usr/share/opensvc/lib/rcUpdatePkgWindows.py  from subprocess import *

repo_subdir = "exe"

def update(fpath):
    cmd = [fpath, '/S']
    print(' '.join(cmd))
    p = Popen(cmd)
    p.communicate()
    return p.returncode
  0707010005c98d000081b40000000000000000000000015ab217540000783b000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/rcUtilities.py from __future__ import print_function
import os, sys
import datetime
import time
import logging
import socket
import select
import shlex
import re
import rcExceptions as ex
from subprocess import *
from rcGlobalEnv import rcEnv
from functools import wraps
import lock
import json
import ast
import operator as op

# supported operators in arithmetic expressions
operators = {
    ast.Add: op.add,
    ast.Sub: op.sub,
    ast.Mult: op.mul,
    ast.Div: op.truediv,
    ast.Pow: op.pow,
    ast.BitOr: op.or_,
    ast.BitAnd: op.and_,
    ast.BitXor: op.xor,
    ast.USub: op.neg,
    ast.FloorDiv: op.floordiv,
    ast.Mod: op.mod,
    ast.Not: op.not_,
    ast.Eq: op.eq,
    ast.NotEq: op.ne,
    ast.Lt: op.lt,
    ast.LtE: op.le,
    ast.Gt: op.gt,
    ast.GtE: op.ge,
    ast.In: op.contains,
}

def eval_expr(expr):
    """ arithmetic expressions evaluator
    """
    def eval_(node):
        _safe_names = {'None': None, 'True': True, 'False': False}
        if isinstance(node, ast.Num): # <number>
            return node.n
        elif isinstance(node, ast.Str):
            return node.s
        elif isinstance(node, ast.Name):
            if node.id in _safe_names:
                return _safe_names[node.id]
            return node.id
        elif isinstance(node, ast.Tuple):
            return tuple(node.elts)
        elif isinstance(node, ast.BinOp): # <left> <operator> <right>
            return operators[type(node.op)](eval_(node.left), eval_(node.right))
        elif isinstance(node, ast.UnaryOp): # <operator> <operand> e.g., -1
            return operators[type(node.op)](eval_(node.operand))
        elif isinstance(node, ast.BoolOp):  # Boolean operator: either "and" or "or" with two or more values
            if type(node.op) == ast.And:
                return all(eval_(val) for val in node.values)
            else:  # Or:
                for val in node.values:
                    result = eval_(val)
                    if result:
                        return result
                    return result  # or returns the final value even if it's falsy
        elif isinstance(node, ast.Compare):  # A comparison expression, e.g. "3 > 2" or "5 < x < 10"
            left = eval_(node.left)
            for comparison_op, right_expr in zip(node.ops, node.comparators):
                right = eval_(right_expr)
                if type(comparison_op) == ast.In:
                    if isinstance(right, tuple):
                        if not any(q.id == left for q in right if isinstance(q, ast.Name)):
                            return False
                    else:
                        if not operators[type(comparison_op)](right, left):
                            return False
                else:
                    if not operators[type(comparison_op)](left, right):
                        return False
                left = right
                return True
        elif isinstance(node, ast.Attribute):
            raise TypeError("strings with dots need quoting")
        elif hasattr(ast, "NameConstant") and isinstance(node, ast.NameConstant):
            return node.value
        else:
            raise TypeError("unsupported node type %s" % type(node))
    return eval_(ast.parse(expr, mode='eval').body)


PROTECTED_DIRS = [
    '/',
    '/bin',
    '/boot',
    '/dev',
    '/dev/pts',
    '/dev/shm',
    '/home',
    '/opt',
    '/proc',
    '/sys',
    '/tmp',
    '/usr',
    '/var',
]

if os.name == 'nt':
    close_fds = False
else:
    close_fds = True

#############################################################################
#
# Cached functions
#
#############################################################################
def fcache(fn):
    """
    A decorator for caching the result of a function
    """
    attr_name = '_fcache_' + fn.__name__
    def _fcache(self):
        if not hasattr(self, attr_name):
            setattr(self, attr_name, fn(self))
        return getattr(self, attr_name)
    return _fcache

def fcache_initialized(self, attr):
    """
    Return True if the function has already been cached
    """
    attr_name = '_fcache_' + attr
    if hasattr(self, attr_name):
        return True
    return False

def unset_fcache(self, attr):
    """
    Unset <attr> function cache
    """
    attr_name = '_lazy_' + attr
    if hasattr(self, attr_name):
        delattr(self, attr_name)

#############################################################################
#
# Lazy properties
#
#############################################################################
def lazy(fn):
    """
    A decorator for on-demand initialization of a property
    """
    attr_name = '_lazy_' + fn.__name__
    @property
    def _lazyprop(self):
        if not hasattr(self, attr_name):
            setattr(self, attr_name, fn(self))
        return getattr(self, attr_name)
    return _lazyprop

def lazy_initialized(self, attr):
    """
    Return True if the lazy property has been initialized
    """
    attr_name = '_lazy_' + attr
    if hasattr(self, attr_name):
        return True
    return False

def set_lazy(self, attr, value):
    """
    Set a <value> as the <self> object lazy property hidden property value
    """
    attr_name = '_lazy_' + attr
    setattr(self, attr_name, value)

def unset_all_lazy(self):
    """
    Unset all lazy property hidden property, iow flush the cache
    """
    for attr in [attr for attr in self.__dict__]:
        if attr.startswith("_lazy_"):
            delattr(self, attr)

def unset_lazy(self, attr):
    """
    Unset <attr> lazy property hidden property, iow flush the cache
    """
    attr_name = '_lazy_' + attr
    if hasattr(self, attr_name):
        delattr(self, attr_name)

def bencode(buff):
    """
    Try a bytes cast, which only work in python3.
    """
    try:
        return bytes(buff, "utf-8")
    except TypeError:
        return buff

def bdecode(buff):
    """
    On python, convert bytes to string using utf-8 and ascii as a fallback
    """
    if buff is None:
        return buff
    if sys.version_info[0] < 3:
        return buff
    if type(buff) == str:
        return buff
    else:
        try:
            return str(buff, "utf-8")
        except:
            return str(buff, "ascii")
    return buff

def is_string(s):
    """
    python[23] compatible string-type test
    """
    if sys.version_info[0] == 2:
        l = (str, unicode)
    else:
        l = (str)
    if isinstance(s, l):
        return True
    return False

def mimport(*args, **kwargs):
    def fmt(s):
        if len(s) > 1:
            return s[0].upper()+s[1:].lower()
        elif len(s) == 1:
            return s[0].upper()
        else:
            return ""

    mod = ""
    for i, e in enumerate(args):
        if e in ("res", "prov", "check") and i == 0:
            mod += e
        else:
            mod += fmt(e)

    try:
        return __import__(mod+rcEnv.sysname)
    except ImportError:
        pass

    try:
        return __import__(mod)
    except ImportError as exc:
        pass

    if kwargs.get("fallback", True) and len(args) > 1:
        args = args[:-1]
        return mimport(*args, **kwargs)
    else:
        raise ImportError("no module found")

def ximport(base):
    mod = base + rcEnv.sysname
    try:
        m = __import__(mod)
        return m
    except:
        pass

    return __import__(base)

def check_privs():
    if os.name == 'nt':
        return
    if os.geteuid() == 0:
        return
    import copy
    l = copy.copy(sys.argv)
    env = rcEnv.initial_env
    if env.get("OSVC_SERVICE_LINK"):
        l[0] = os.path.join(rcEnv.paths.pathetc, env.get("OSVC_SERVICE_LINK"))
    else:
        l[0] = os.path.basename(l[0]).replace(".py", "")
    if which("sudo"):
        os.execvpe("sudo", ["sudo"] + l, env=env)
    elif which("pfexec"):
        os.execvpe("sudo", ["pfexec"] + l, env=env)
    else:
        print("Insufficient privileges")
        sys.exit(1)

def banner(text, ch='=', length=78):
    spaced_text = ' %s ' % text
    banner = spaced_text.center(length, ch)
    return banner

def is_exe(fpath, realpath=False):
    """Returns True if file path is executable, False otherwize
    """
    if realpath:
        fpath = os.path.realpath(fpath)
    if os.path.isdir(fpath):
        return False
    return os.path.exists(fpath) and os.access(fpath, os.X_OK)

def which(program):
    if program is None:
        return
    def ext_candidates(fpath):
        yield fpath
        for ext in os.environ.get("PATHEXT", "").split(os.pathsep):
            yield fpath + ext

    fpath, fname = os.path.split(program)
    if fpath:
        if os.path.isfile(program) and is_exe(program, realpath=True):
            return program
    else:
        for path in os.environ["PATH"].split(os.pathsep):
            exe_file = os.path.join(path, program)
            for candidate in ext_candidates(exe_file):
                if is_exe(candidate, realpath=True):
                    return candidate

    return

def justcall(argv=['/bin/false'], stdin=None):
    """subprosses call argv, return (stdout,stderr,returncode)
    """
    if which(argv[0]) is None:
        return ("", "", 1)
    process = Popen(argv, stdin=stdin, stdout=PIPE, stderr=PIPE, close_fds=close_fds)
    stdout, stderr = process.communicate(input=None)
    return bdecode(stdout), bdecode(stderr), process.returncode

def empty_string(buff):
    b = buff.strip(' ').strip('\n')
    if len(b) == 0:
        return True
    return False

def lcall(cmd, logger, outlvl=logging.INFO, errlvl=logging.ERROR, timeout=None, **kwargs):
    """
    Variant of subprocess.call that accepts a logger instead of stdout/stderr,
    and logs stdout messages via logger.debug and stderr messages via
    logger.error.
    """
    start = time.time()
    proc = Popen(cmd, stdout=PIPE, stderr=PIPE, **kwargs)
    log_level = {proc.stdout: outlvl, proc.stderr: errlvl}
    terminated = False
    killed = False

    def check_io():
        logged = 0
        ready_to_read = select.select([proc.stdout, proc.stderr], [], [], 1000)[0]
        for io in ready_to_read:
            line = io.readline()
            if sys.version_info[0] < 3:
                line = line.decode("utf8")
            else:
                line = line.decode("ascii", errors="ignore")
            if line in ('', b''):
                continue
            logger.log(log_level[io], line[:-1])
            logged += 1
        return logged

    # keep checking stdout/stderr until the proc exits
    while proc.poll() is None:
        check_io()
        ellapsed = time.time() - start
        if timeout and ellapsed > timeout:
            if not terminated:
                logger.error("execution timeout (%d seconds). send SIGTERM." % timeout)
                proc.terminate()
                terminated = True
            elif not killed and ellapsed > timeout*2:
                logger.error("SIGTERM handling timeout (%d seconds). send SIGKILL." % timeout)
                proc.kill()
                killed = True

    while True:
        # check again to catch anything after the process exits
        logged = check_io()
        if logged == 0:
            break
    return proc.wait()


def call(argv,
         cache=False,      # serve/don't serve cmd output from cache
         log=None,         # callers should provide there own logger
                           # or we'll have to allocate a generic one

         info=False,       # False: log cmd as debug
                           # True:  log cmd as info

         outlog=False,     # False: discard stdout

         errlog=True,      # False: discard stderr
                           # True:  log stderr as err, warn or info
                           #        depending on err_to_warn and
                           #        err_to_info value

         outdebug=True,    # True:  log.debug cmd stdout
                           # False: skip log.debug stdout

         errdebug=True,    # True:  log.debug cmd stderr
                           # False: skip log.debug stderr
                           #        depending on err_to_warn and
                           #        err_to_info value
         err_to_warn=False,
         err_to_info=False,
         warn_to_info=False,
         shell=False,
         stdin=None,
         preexec_fn=None,
         cwd=None,
         env=None):
    """ return(ret, out,err)
    """
    if log is None:
        log = logging.getLogger('CALL')

    if not argv or len(argv) == 0:
        return (0, '', '')

    if shell:
        cmd = argv
    else:
        cmd = ' '.join(argv)
     
    if not shell and which(argv[0]) is None:
        log.error("%s does not exist or not in path or is not executable"%
                  argv[0])
        return (1, '', '')

    if info:
        log.info(cmd)
    else:
        log.debug(cmd)

    if not hasattr(rcEnv, "call_cache"):
        rcEnv.call_cache = {}

    if cache and cmd not in rcEnv.call_cache:
        log.debug("cache miss for '%s'"%cmd)

    if not cache or cmd not in rcEnv.call_cache:
        process = Popen(argv, stdin=stdin, stdout=PIPE, stderr=PIPE, close_fds=close_fds, shell=shell, preexec_fn=preexec_fn, cwd=cwd, env=env)
        buff = process.communicate()
        buff = tuple(map(lambda x: bdecode(x), buff))
        ret = process.returncode
        if ret == 0:
            if cache:
                log.debug("store '%s' output in cache"%cmd)
                rcEnv.call_cache[cmd] = buff
        elif cmd in rcEnv.call_cache:
            log.debug("discard '%s' output from cache because ret!=0"%cmd)
            del rcEnv.call_cache[cmd]
        elif cache:
            log.debug("skip store '%s' output in cache because ret!=0"%cmd)
    else:
        log.debug("serve '%s' output from cache"%cmd)
        buff = rcEnv.call_cache[cmd]
        ret = 0
    if not empty_string(buff[1]):
        if err_to_info:
            log.info('stderr:')
            for line in buff[1].split("\n"):
                log.info(line)
        elif err_to_warn:
            log.warning('stderr:')
            for line in buff[1].split("\n"):
                log.warning(line)
        elif errlog:
            if ret != 0:
                log.error('stderr:')
                for line in buff[1].split("\n"):
                    log.error(line)
            elif warn_to_info:
                log.info('command successful but stderr:')
                for line in buff[1].split("\n"):
                    log.info(line)
            else:
                log.warning('command successful but stderr:')
                for line in buff[1].split("\n"):
                    log.warning(line)
        elif errdebug:
            log.debug('stderr:')
            for line in buff[1].split("\n"):
                log.debug(line)
    if not empty_string(buff[0]):
        if outlog:
            if ret == 0:
                log.info('output:')
                for line in buff[0].split("\n"):
                    log.info(line)
            elif err_to_info:
                log.info('command failed with stdout:')
                for line in buff[0].split("\n"):
                    log.info(line)
            elif err_to_warn:
                log.warning('command failed with stdout:')
                for line in buff[0].split("\n"):
                    log.warning(line)
            else:
                log.error('command failed with stdout:')
                for line in buff[0].split("\n"):
                    log.error(line)
        elif outdebug:
            log.debug('output:')
            for line in buff[0].split("\n"):
                log.debug(line)

    return (ret, buff[0], buff[1])

def qcall(argv=['/bin/false']) :
    """qcall Launch Popen it args disgarding output and stderr"""
    if not argv:
        return (0, '')
    process = Popen(argv, stdout=PIPE, stderr=PIPE, close_fds=close_fds)
    process.wait()
    return process.returncode

def vcall(args, **kwargs):
    kwargs["info"] = True
    kwargs["outlog"] = True
    return call(args, **kwargs)

def getmount(path):
    path = os.path.abspath(path)
    while path != os.path.sep:
        if not os.path.islink(path) and os.path.ismount(path):
            return path
        path = os.path.abspath(os.path.join(path, os.pardir))
    return path

def protected_dir(path):
    path = path.rstrip("/")
    if path in PROTECTED_DIRS:
        return True
    return False

def protected_mount(path):
    mount = getmount(path)
    if mount in PROTECTED_DIRS:
        return True
    return False

def printplus(obj):
    """
    Pretty-prints the object passed in.

    """
    # Dict
    if isinstance(obj, dict):
        for k, v in sorted(obj.items()):
            print("%s: %s" % (str(k), str(v)))

    # List or tuple
    elif isinstance(obj, list) or isinstance(obj, tuple):
        for x in obj:
            print(x)

    # Other
    else:
        print(obj)

def cmdline2list(cmdline):
    """
    Translate a command line string into a list of arguments, using
    using the same rules as the MS C runtime:

    1) Arguments are delimited by white space, which is either a
       space or a tab.

    2) A string surrounded by double quotation marks is
       interpreted as a single argument, regardless of white space
       contained within.  A quoted string can be embedded in an
       argument.

    3) A double quotation mark preceded by a backslash is
       interpreted as a literal double quotation mark.

    4) Backslashes are interpreted literally, unless they
       immediately precede a double quotation mark.

    5) If backslashes immediately precede a double quotation mark,
       every pair of backslashes is interpreted as a literal
       backslash.  If the number of backslashes is odd, the last
       backslash escapes the next double quotation mark as
       described in rule 3.
    """

    # See
    # http://msdn.microsoft.com/library/en-us/vccelng/htm/progs_12.asp

    # Step 1: Translate all literal quotes into QUOTE.  Justify number
    # of backspaces before quotes.
    tokens = []
    bs_buf = ""
    QUOTE = 1 # \", literal quote
    for c in cmdline:
        if c == '\\':
            bs_buf += c
        elif c == '"' and bs_buf:
            # A quote preceded by some number of backslashes.
            num_bs = len(bs_buf)
            tokens.extend(["\\"] * (num_bs//2))
            bs_buf = ""
            if num_bs % 2:
                # Odd.  Quote should be placed literally in array
                tokens.append(QUOTE)
            else:
                # Even.  This quote serves as a string delimiter
                tokens.append('"')

        else:
            # Normal character (or quote without any preceding
            # backslashes)
            if bs_buf:
                # We have backspaces in buffer.  Output these.
                tokens.extend(list(bs_buf))
                bs_buf = ""

            tokens.append(c)

    # Step 2: split into arguments
    result = [] # Array of strings
    quoted = False
    arg = [] # Current argument
    tokens.append(" ")
    for c in tokens:
        if c == '"':
            # Toggle quote status
            quoted = not quoted
        elif c == QUOTE:
            arg.append('"')
        elif c in (' ', '\t'):
            if quoted:
                arg.append(c)
            else:
                # End of argument.  Output, if anything.
                if arg:
                    result.append(''.join(arg))
                    arg = []
        else:
            # Normal character
            arg.append(c)

    return result

def action_triggers(self, trigger="", action=None, **kwargs):
    """
    Executes a service or resource trigger. Guess if the shell mode is needed
    from the trigger syntax.
    """

    actions = [
        'provision',
        'unprovision',
        'start',
        'stop',
        'shutdown',
        'sync_nodes',
        'sync_drp',
        'sync_all',
        'sync_resync',
        'sync_update',
        'sync_restore',
        'run',
        'command', # tasks use that as an action
    ]

    compat_triggers = [
        'pre_syncnodes', 'pre_syncdrp',
        'post_syncnodes', 'post_syncdrp',
        'post_syncresync', 'pre_syncresync',
        'post_syncupdate', 'pre_syncupdate',
    ]

    def get_trigger_cmdv(cmd, kwargs):
        """
        Return the cmd arg useable by subprocess Popen
        """
        if not kwargs.get("shell", False):
            if sys.version_info[0] < 3:
                cmdv = shlex.split(cmd.encode('utf8'))
                cmdv = [elem.decode('utf8') for elem in cmdv]
            else:
                cmdv = shlex.split(cmd)
        else:
            cmdv = cmd
        return cmdv

    if hasattr(self, "svc"):
        svc = self.svc
        section = self.rid
    else:
        svc = self
        section = "DEFAULT"

    if action not in actions:
        return
    elif action == "startstandby":
        action = "start"
    elif action == "shutdown":
        action = "stop"

    if "blocking" in kwargs:
        blocking = kwargs["blocking"]
        del kwargs["blocking"]
    else:
        blocking = False

    if trigger == "":
        attr = action
    else:
        attr = trigger+"_"+action

    # translate deprecated actions
    if attr in compat_triggers:
        attr = compat_triggers[attr]

    try:
        cmd = svc.conf_get(section, attr, use_default=False)
    except ValueError:
        # no corresponding keyword
        return
    except ex.OptNotFound:
        return

    if "|" in cmd or "&&" in cmd or ";" in cmd:
        kwargs["shell"] = True

    cmdv = get_trigger_cmdv(cmd, kwargs)

    self.log.info("%s: %s", attr, cmd)

    if svc.options.dry_run:
        return

    try:
        ret = self.lcall(cmdv, **kwargs)
    except OSError as exc:
        ret = 1
        if exc.errno == 8:
            self.log.error("%s exec format error: check the script shebang", cmd)
        else:
            self.log.error("%s error: %s", cmd, str(exc))
    except Exception as exc:
        ret = 1
        self.log.error("%s error: %s", cmd, str(exc))

    if blocking and ret != 0:
        raise ex.excError("%s trigger %s blocking error" % (trigger, cmd))


def try_decode(string, codecs=['utf8', 'latin1']):
    for i in codecs:
        try:
            return string.decode(i)
        except:
            pass
    return string

def getaddr_cache_set(name, addr):
    cache_d = os.path.join(rcEnv.paths.pathvar, "cache", "addrinfo")
    if not os.path.exists(cache_d):
        os.makedirs(cache_d)
    cache_f = os.path.join(cache_d, name)
    with open(cache_f, 'w') as f:
        f.write(addr)
    return addr

def getaddr_cache_get(name):
    cache_d = os.path.join(rcEnv.paths.pathvar, "cache", "addrinfo")
    if not os.path.exists(cache_d):
        os.makedirs(cache_d)
    cache_f = os.path.join(cache_d, name)
    if not os.path.exists(cache_f):
        raise Exception("addrinfo cache empty for name %s" % name)
    cache_mtime = datetime.datetime.fromtimestamp(os.stat(cache_f).st_mtime)
    limit_mtime = datetime.datetime.now() - datetime.timedelta(minutes=16)
    if cache_mtime < limit_mtime:
        raise Exception("addrinfo cache expired for name %s (%s)" % (name, cache_mtime.strftime("%Y-%m-%d %H:%M:%S")))
    with open(cache_f, 'r') as f:
        addr = f.read()
    if addr.count(".") != 3 and ":" not in addr:
        raise Exception("addrinfo cache corrupted for name %s: %s" % (name, addr))
    return addr

def getaddr(name, cache_fallback, log=None):
    if cache_fallback:
        return getaddr_caching(name, log=log)
    else:
        return getaddr_non_caching(name)

def getaddr_non_caching(name, log=None):
    a = socket.getaddrinfo(name, None)
    if len(a) == 0:
        raise Exception("could not resolve name %s: empty dns request resultset" % name)
    addr = a[0][4][0]
    try:
        getaddr_cache_set(name, addr)
    except Exception as e:
        if log:
            log.warning("failed to cache name addr %s, %s: %s"  %(name, addr, str(e)))
    return addr

def getaddr_caching(name, log=None):
    try:
        addr = getaddr_non_caching(name)
    except Exception as e:
        if log:
            log.warning("%s. fallback to cache." % str(e))
        addr = getaddr_cache_get(name)
    if log:
        log.info("fetched %s address for name %s from cache" % (addr, name))
    return addr

def cidr_to_dotted(s):
    i = int(s)
    _in = ""
    _out = ""
    for i in range(i):
        _in += "1"
    for i in range(32-i):
        _in += "0"
    _out += str(int(_in[0:8], 2))+'.'
    _out += str(int(_in[8:16], 2))+'.'
    _out += str(int(_in[16:24], 2))+'.'
    _out += str(int(_in[24:32], 2))
    return _out

def to_dotted(s):
    s = str(s)
    if '.' in s:
        return s
    return cidr_to_dotted(s)

def hexmask_to_dotted(mask):
    mask = mask.replace('0x', '')
    s = [str(int(mask[i:i+2], 16)) for i in range(0, len(mask), 2)]
    return '.'.join(s)

def dotted_to_cidr(mask):
    if mask is None:
        return ''
    cnt = 0
    l = mask.split(".")
    l = map(lambda x: int(x), l)
    for a in l:
        cnt += str(bin(a)).count("1")
    return str(cnt)

def to_cidr(s):
    if '.' in s:
        return dotted_to_cidr(s)
    elif re.match("^(0x)*[0-9a-f]{8}$", s):
        # example: 0xffffff00
        s = hexmask_to_dotted(s)
        return dotted_to_cidr(s)
    return s

def term_width():
    default = 78
    try:
        # python 3.3+
        return os.get_terminal_size().columns
    except:
        pass
    if rcEnv.sysname == "Windows":
        return default
    if which("stty") is None:
        return default
    out, err, ret = justcall(['stty', '-a'])
    m = re.search('columns\s+(?P<columns>\d+);', out)
    if m:
        return int(m.group('columns'))
    try:
        return int(os.environ["COLUMNS"])
    except Exception as e:
        pass
    return default

def get_cache_d():
    return os.path.join(rcEnv.paths.pathvar, "cache", rcEnv.session_uuid)

def cache(sig):
    def wrapper(fn):
        @wraps(fn)
        def decorator(*args, **kwargs):
            if len(args) > 0 and hasattr(args[0], "log"):
                log = args[0].log
            else:
                log = None

            if len(args) > 0 and hasattr(args[0], "cache_sig_prefix"):
                _sig = args[0].cache_sig_prefix + sig
            else:
                _sig = sig

            fpath = cache_fpath(_sig)

            try:
                lfd = lock.lock(timeout=30, delay=0.1, lockfile=fpath+'.lock', intent="cache")
            except Exception as e:
                if log:
                    log.warning("cache locking error: %s. run command uncached." % str(e))
                return fn(*args, **kwargs)
            try:
                data = cache_get(fpath, log=log)
            except Exception as e:
                if log:
                    log.debug(str(e))
                data = fn(*args, **kwargs)
                cache_put(fpath, data, log=log)
            lock.unlock(lfd)
            return data
        return decorator
    return wrapper

def cache_fpath(sig):
    cache_d = get_cache_d()
    if not os.path.exists(cache_d):
        try:
            os.makedirs(cache_d)
        except:
            # we run unlocked here ...
            # another process created the dir since we tested ?
            pass
    fpath = os.path.join(cache_d, sig)
    return fpath

def cache_put(fpath, data, log=None):
    if log:
        log.debug("cache PUT: %s" % fpath)
    try:
        with open(fpath, "w") as f:
            json.dump(data, f)
    except Exception as e:
        try:
            os.unlink(fpath)
        except:
            pass
    return data

def cache_get(fpath, log=None):
    if not os.path.exists(fpath):
        raise Exception("cache MISS: %s" % fpath)
    if log:
        log.debug("cache GET: %s" % fpath)
    try:
        with open(fpath, "r") as f:
            data = json.load(f)
    except Exception as e:
        raise ex.excError("cache read error: %s" % str(e))
    return data

def clear_cache(sig, o=None):
    if o and hasattr(o, "cache_sig_prefix"):
        sig = o.cache_sig_prefix + sig
    fpath = cache_fpath(sig)
    if not os.path.exists(fpath):
        return
    if o and hasattr(o, "log"):
        o.log.debug("cache CLEAR: %s" % fpath)
    lfd = lock.lock(timeout=30, delay=0.1, lockfile=fpath+'.lock')
    try:
        os.unlink(fpath)
    except:
        pass
    lock.unlock(lfd)

def purge_cache():
    cache_d = get_cache_d()
    try:
        shutil.rmtree(d)
    except:
        pass

def purge_cache_expired():
    import time
    import shutil
    cache_d = os.path.join(rcEnv.paths.pathvar, "cache")
    if not os.path.exists(cache_d) or not os.path.isdir(cache_d):
        return
    for d in os.listdir(cache_d): 
        d = os.path.join(cache_d, d)
        if not os.path.isdir(d) or not os.stat(d).st_ctime < time.time()-(21600): 
            # session more recent than 6 hours
            continue
        try:
            shutil.rmtree(d)
        except:
            pass

def read_cf(fpath, defaults=None):
    """
    Read and parse an arbitrary ini-formatted config file, and return
    the RawConfigParser object.
    """
    import codecs
    from rcConfigParser import RawConfigParser
    try:
        from collections import OrderedDict
        config = RawConfigParser(dict_type=OrderedDict)
    except ImportError:
        config = RawConfigParser()

    if defaults is None:
        defaults = {}
    config = RawConfigParser(defaults)
    if not os.path.exists(fpath):
        return config
    with codecs.open(fpath, "r", "utf8") as ofile:
        if sys.version_info[0] >= 3:
            config.read_file(ofile)
        else:
            config.readfp(ofile)
    return config

def drop_option(option, cmd, drop_value=False):
    """
    Drop an option, and its value if requested, from an argv
    """
    to_drop = []
    for i, word in enumerate(cmd):
        if word == option:
            if drop_value:
                to_drop += [i, i+1]
            else:
                to_drop += [i]
            continue
        if word.startswith(option+"="):
            to_drop += [i]
            continue
    for idx in sorted(to_drop, reverse=True):
        del cmd[idx]
    return cmd

def fsum(fpath):
    """
    Return a file content checksum
    """
    import hashlib
    import codecs
    with codecs.open(fpath, "r", "utf-8") as filep:
        buff = filep.read()
    cksum = hashlib.md5(buff.encode("utf-8"))
    return cksum.hexdigest()

if __name__ == "__main__":
    #print("call(('id','-a'))")
    #(r,output,err)=call(("/usr/bin/id","-a"))
    #print("status: ", r, "output:", output)
    pass
 0707010005c98e000081b40000000000000000000000015ab217540000017b000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/rcUtilitiesAIX.py  import os
import re
from rcUtilities import call

def check_ping(addr, timeout=5, count=1):
    cmd = ['ping', '-c', repr(count),
                   '-w', repr(timeout)]
    if ':' in addr:
        cmd += ['-a', 'inet6']
    cmd += [addr]
    (ret, out, err) = call(cmd)
    if ret == 0:
        return True
    return False

def devs_to_disks(self, devs=set()):
    return devs
 0707010005c991000081b40000000000000000000000015ab217540000013b000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/rcUtilitiesHP-UX.py    from rcUtilities import call

def check_ping(addr, timeout=5, count=1):
    cmd = ['ping', addr,
                   '-n', repr(count),
                   '-m', repr(timeout)]
    (ret, out, err) = call(cmd)
    if ret == 0:
        return True
    return False

def devs_to_disks(self, devs=set()):
    return devs
 0707010005c98f000081b40000000000000000000000015ab21754000001ad000000660000000000000000000000000000003000000000root/usr/share/opensvc/lib/rcUtilitiesDarwin.py   from rcUtilities import call, qcall

def check_ping(addr, timeout=5, count=1):
    if ':' in addr:
        ping = 'ping6'
    else:
        ping = 'ping'
    cmd = [ping, '-c', repr(count),
                 '-W', repr(timeout),
                 '-t', repr(timeout),
                 addr]
    (ret, out, err) = call(cmd)
    if ret == 0:
        return True
    return False

def devs_to_disks(self, devs=set()):
    return devs
   0707010005c990000081b40000000000000000000000015ab217540000015a000000660000000000000000000000000000003100000000root/usr/share/opensvc/lib/rcUtilitiesFreeBSD.py  from rcUtilities import call

def check_ping(addr, timeout=5, count=1):
    if ':' in addr:
        cmd = ['ping6']
    else:
        cmd = ['ping', '-W', str(timeout)]
    cmd += ['-c', repr(count), addr]
    (ret, out, err) = call(cmd)
    if ret == 0:
        return True
    return False

def devs_to_disks(self, devs=set()):
    return devs
  0707010005c992000081b40000000000000000000000015ab2175400001cef000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/rcUtilitiesLinux.py    import os
import re
import glob
import time

from rcGlobalEnv import rcEnv
from rcUtilities import call, qcall, justcall, which
import rcExceptions as ex

label_to_dev_cache = {}

def udevadm_settle():
    if not which("udevadm"):
        return
    cmd = ["udevadm", "settle"]
    justcall(cmd)

def dev_to_paths(dev, log=None):
    if dev.startswith("/dev/sd"):
        return [dev]
    if not dev.startswith("/dev/dm-"):
        return []
    name = os.path.basename(dev)
    cmd = ["dmsetup", "table", "-j", str(major("device-mapper")), "-m", dev[8:]]
    out, err, ret = justcall(cmd)
    if ret != 0:
        raise ex.excError(err)
    if "multipath" not in out:
        return []
    paths = ["/dev/"+os.path.basename(_name) for _name in glob.glob("/sys/block/%s/slaves/*" % name)]
    return paths

def dev_is_ro(dev):
    dev = dev.replace('/dev/', '')
    sysdev = "/sys/block/%s/ro"%dev
    with open(sysdev, 'r') as s:
        buff = s.read()
    if buff.strip() == "1":
        return True

def dev_rescan(dev, log=None):
    dev = dev.replace('/dev/', '')
    sysdev = "/sys/block/%s/device/rescan"%dev
    if log:
        log.info("echo 1>%s"%sysdev)
    with open(sysdev, 'w') as s:
        s.write("1")

def refresh_multipath(dev, log=None):
    cmd = [rcEnv.syspaths.multipath, '-v0', '-r', dev]
    (ret, out, err) = call(cmd, info=True, outlog=True, log=log)
    if ret != 0:
        raise ex.excError

def dev_ready(dev, log=None):
    cmd = ['sg_turs', dev]
    (ret, out, err) = call(cmd, info=True, outlog=True, log=log)
    if ret != 0:
        return False
    return True

def wait_for_dev_ready(dev, log=None):
    delay = 1
    timeout = 5
    for i in range(timeout//delay):
        if dev_ready(dev, log=log):
            return
        if i == 0:
            if log:
                log.info("waiting for device %s to become ready (max %i secs)"%(dev,timeout))
        time.sleep(delay)
    if log:
        log.error("timed out waiting for device %s to become ready (max %i secs)"%(dev,timeout))
    raise ex.excError

def promote_dev_rw(dev, log=None):
    for dev in dev_to_paths(dev, log=log):
       count = 0
       if dev_is_ro(dev):
           dev_rescan(dev, log=log)
           wait_for_dev_ready(dev, log=log)
           count += 1
       if count > 0:
           refresh_multipath(dev, log=log)

def loop_is_deleted(dev):
    if not which(rcEnv.syspaths.losetup):
        raise ex.excError("losetup must be installed")
    out, err, ret = justcall([rcEnv.syspaths.losetup, dev])
    if "(deleted)" in out:
        return True
    return False

def label_to_dev(label, tree=None):
    """
       blkid can return a device slave of a drbd, as drbd is
       transparent wrt to signature detection. Detect this case
       and return the holding device. Otherwise return None.
    """
    if label in label_to_dev_cache:
        return label_to_dev_cache[label]

    if not which(rcEnv.syspaths.blkid):
        return
    out, err, ret = justcall([rcEnv.syspaths.blkid, "-t", label])
    if ret != 0:
        return
    devps = []
    for line in out.split("\n"):
        if len(line) == 0:
            continue
        devp = line.split(":")[0]
        if devp.startswith("/dev/loop") and loop_is_deleted(devp):
            continue
        devps.append(devp)

    if len(devps) == 0:
        return
    elif len(devps) == 1:
        return devps[0]

    if tree is None:
        from rcDevTreeLinux import DevTree
        tree = DevTree()
        tree.load()
    devs = tree.get_devs_by_devpaths(devps)
    for dev in devs:
        parent_devps = set()
        for p in dev.parents:
            d = tree.get_dev(p.parent)
            if d is None:
                continue
            parent_devps |= set(d.devpath)
        inter = set(devps) & parent_devps
        if len(inter) > 0:
            devp = "/dev/"+dev.devname
            label_to_dev_cache[label] = devp
            return devp

    raise ex.excError("multiple devs match the label: %s" % ", ".join(devps))

def major(driver):
    path = os.path.join(os.path.sep, 'proc', 'devices')
    try:
        f = open(path)
    except:
        return -1
    for line in f.readlines():
        words = line.split()
        if len(words) == 2 and words[1] == driver:
            f.close()
            return int(words[0])
    f.close()
    return -1

def get_blockdev_sd_slaves(syspath):
    slaves = set()
    if not os.path.exists(syspath):
        return slaves
    for s in os.listdir(syspath):
        if re.match('^sd[a-z]*', s) is not None:
            slaves.add('/dev/' + s)
            continue
        deeper = os.path.join(syspath, s, 'slaves')
        if os.path.isdir(deeper):
            slaves |= get_blockdev_sd_slaves(deeper)
    return slaves

def check_ping(addr, timeout=5, count=1):
    if ':' in addr:
        ping = 'ping6'
    else:
        ping = 'ping'
    cmd = [ping, '-c', repr(count),
                 '-W', repr(timeout),
                 '-w', repr(timeout),
                 addr]
    out, err, ret = justcall(cmd)
    if ret == 0:
        return True
    return False

def lv_exists(self, device):
    if qcall([rcEnv.syspaths.lvs, device]) == 0:
        return True
    return False

def lv_info(self, device):
    cmd = [
        rcEnv.syspaths.lvs,
        '-o', 'vg_name,lv_name,lv_size',
        '--noheadings', '--units', 'm',
        device
    ]
    ret, buff, err = self.call(cmd)
    if ret != 0:
        return (None, None, None)
    info = buff.split()
    if 'M' in info[2]:
        lv_size = float(info[2].split('M')[0])
    elif 'm' in info[2]:
        lv_size = float(info[2].split('m')[0])
    else:
        self.log.error("%s output does not have the expected unit (m or M)"%' '.join(cmd))
        ex.excError
    return (info[0], info[1], lv_size)

def get_partition_parent(dev):
    syspath = '/sys/block/*/' + os.path.basename(dev)
    l = glob.glob(syspath)
    if len(l) == 1:
        return '/dev/'+l[0].split('/')[3]
    return None

def devs_to_disks(self, devs=set()):
    """ If PV is a device map, replace by its sysfs name (dm-*)
        If device map has slaves, replace by its slaves
    """
    disks = set()
    dm_major = major('device-mapper')
    try: md_major = major('md')
    except: md_major = 0
    try: lo_major = major('loop')
    except: lo_major = 0
    for dev in devs:
        try:
            statinfo = os.stat(dev)
        except:
            self.log.debug("can not stat %s" % dev)
            continue
        if md_major != 0 and os.major(statinfo.st_rdev) == md_major:
            md = dev.replace("/dev/", "")
            syspath = '/sys/block/' + md + '/slaves'
            disks |= get_blockdev_sd_slaves(syspath)
        elif os.major(statinfo.st_rdev) == dm_major:
            dm = 'dm-' + str(os.minor(statinfo.st_rdev))
            syspath = '/sys/block/' + dm + '/slaves'
            disks |= get_blockdev_sd_slaves(syspath)
        elif lo_major != 0 and os.major(statinfo.st_rdev) == lo_major:
            self.log.debug("skip loop device %s from disk list"%dev)
            pass
        else:
            parent = get_partition_parent(dev)
            if parent is not None:
                disks.add(parent)
            else:
                disks.add(dev)
    _disks = list(disks)
    for i, disk in enumerate(_disks):
        _disks[i] = re.sub("^(/dev/[vhs]d[a-z]*)[0-9]*$", r"\1", disk)
    return disks

 0707010005c993000081b40000000000000000000000015ab217540000013b000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/rcUtilitiesOSF1.py from rcUtilities import call

def check_ping(addr, timeout=5, count=1):
    cmd = ['ping', '-c', repr(count),
                   '-t', repr(timeout),
                   addr]
    (ret, out, err) = call(cmd)
    if ret == 0:
        return True
    return False

def devs_to_disks(self, devs=set()):
    return devs
 0707010005c994000081b40000000000000000000000015ab217540000052d000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/rcUtilitiesSunOS.py    from rcUtilities import justcall

def check_ping(addr, timeout=5, count=1):
    cmd = ['ping', addr, "%s" % timeout]
    out, err, ret = justcall(cmd)
    if ret == 0:
        return True
    return False

def get_os_ver():
    cmd = ['uname', '-v']
    out, err, ret = justcall(cmd)
    if ret != 0:
        return self.undef
    lines = out.split('\n')
    if len(lines) == 0:
        return self.undef
    try:
        osver = float(lines[0])
    except:
        osver = 0
    return osver

# Solaris 2.6 is SunOS 5.6 : osver = 6.0
# Solaris 7 is SunOS 5.7   : osver = 7.0
# Solaris 8 is SunOS 5.8   : osver = 8.0
# ...
# Solaris 11 is SunOS 5.11 : osver = 11.0
# Solaris 11U1 is SunOS 5.11 : osver = 11.1
# Solaris 11U2 is SunOS 5.11 : osver = 11.2
def get_solaris_version():
    cmd = ['uname', '-r']
    out, err, ret = justcall(cmd)
    if ret != 0:
        return self.undef
    lines = out.split('\n')
    if len(lines) == 0:
        return self.undef
    try:
        base, osver = lines[0].split('.')
        osver = int(osver)
    except:
        osver = 0

    if osver >= '11':
        cmd = ['uname', '-v']
        out, err, ret = justcall(cmd)
        if ret == 0:
            lines = out.split('\n')
            osver = lines[0]

    return float(osver)

def devs_to_disks(self, devs=set()):
    return devs
   0707010005c995000081b40000000000000000000000015ab217540000033a000000660000000000000000000000000000003100000000root/usr/share/opensvc/lib/rcUtilitiesWindows.py  import os
import re
from rcUtilities import justcall
import string
from ctypes import windll


def check_ping(addr, timeout=5, count=1):
    ping = 'ping.exe'
    cmd = [ping,
           '-n', repr(count),
           '-w', repr(timeout),
           addr]
    out, err, ret = justcall(cmd)
    if ret == 0:
        return True
    return False

def get_registry_value(key, subkey, value):
    import _winreg
    key = getattr(_winreg, key)
    handle = _winreg.OpenKey(key, subkey)
    (value, type) = _winreg.QueryValueEx(handle, value)
    return value

def get_drives():
    drives = []
    bitmask = windll.kernel32.GetLogicalDrives()
    for letter in string.uppercase:
        if bitmask & 1:
            drives.append(letter)
        bitmask >>= 1
    return drives

def devs_to_disks(self, devs=set()):
    return devs
  0707010005c996000081b40000000000000000000000015ab2175400000cee000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/rcVioServer.py import os

try:
    import ConfigParser
except ImportError:
    import configparser as ConfigParser

import rcExceptions as ex
from rcGlobalEnv import rcEnv
from rcUtilities import justcall, which

if rcEnv.paths.pathbin not in os.environ['PATH']:
    os.environ['PATH'] += ":"+rcEnv.paths.pathbin

def rcmd(cmd, manager, username, key):
    _cmd = ['ssh', '-i', key, '@'.join((username, manager))]
    _cmd += [cmd]
    out, err, ret = justcall(_cmd)
    if ret != 0:
        print(' '.join(_cmd))
        print(out)
        raise ex.excError("ssh command execution error")
    return out, err

class VioServers(object):
    def __init__(self, objects=[]):
        self.objects = []
        if len(objects) > 0:
            self.filtering = True
        else:
            self.filtering = False
        self.arrays = []
        cf = rcEnv.paths.authconf
        if not os.path.exists(cf):
            return
        conf = ConfigParser.RawConfigParser()
        conf.read(cf)
        m = {}
        for s in conf.sections():
            if self.filtering and s not in self.objects:
                continue
            if not conf.has_option(s, "type") or \
               conf.get(s, "type") != "vioserver":
                continue
            try:
                username = conf.get(s, 'username')
                key = conf.get(s, 'key')
                m[s] = [username, key]
            except:
                print("error parsing section", s)
                pass
        del(conf)
        for name, creds in m.items():
            username, key = creds
            self.arrays.append(VioServer(name, username, key))

    def __iter__(self):
        for array in self.arrays:
            yield(array)

class VioServer(object):
    def __init__(self, name, username, key):
        self.name = name
        self.username = username
        self.key = key
        self.keys = ['lsmap', 'bootinfo', 'lsfware', 'lsdevattr', 'lsdevvpd', 'devsize']

    def rcmd(self, cmd):
        return rcmd(cmd, self.name, self.username, self.key)

    def get_lsmap(self):
        cmd = 'ioscli lsmap -all -fmt :'
        print("%s: %s"%(self.name, cmd))
        return self.rcmd(cmd)[0]

    def get_bootinfo(self):
        cmd = 'for i in $(ioscli lsmap -all -field backing|sed "s/Backing device//"); do echo $i $(bootinfo -s $i) ; done'
        print("%s: %s"%(self.name, cmd))
        return self.rcmd(cmd)[0]

    def get_lsfware(self):
        cmd = 'ioscli lsfware'
        print("%s: %s"%(self.name, cmd))
        return self.rcmd(cmd)[0]

    def get_lsdevattr(self):
        cmd = 'for i in $(ioscli lsdev -type disk -field name -fmt .) ; do echo $i $(ioscli lsdev -dev $i -attr|grep ww_name);done'
        print("%s: %s"%(self.name, cmd))
        return self.rcmd(cmd)[0]

    def get_lsdevvpd(self):
        cmd = 'for i in $(ioscli lsdev -type disk -field name -fmt .) ; do echo $i ; ioscli lsdev -dev $i -vpd;done'
        print("%s: %s"%(self.name, cmd))
        return self.rcmd(cmd)[0]

    def get_devsize(self):
        cmd = 'for i in $(ioscli lsdev -type disk -field name -fmt .) ; do echo $i $(bootinfo -s $i);done'
        print("%s: %s"%(self.name, cmd))
        return self.rcmd(cmd)[0]

if __name__ == "__main__":
    o = VioServers()
    for vioserver in o:
        print(vioserver.lsmap())
  0707010005c997000081b40000000000000000000000015ab2175400000591000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/rcWakeOnLan.py import sys
import socket
import struct
import re

regex_mac = re.compile('[0-9a-f]{2}([-:])[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$')
regex_broadcast = re.compile('^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$')

class wolrequest(object):
    def __init__(self, macaddress, broadcast, udpport=7):
        self.mac = macaddress
        self.broadcast = broadcast
        self.udpport = int(udpport)
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

    def check_mac(self):
        if not ':' and not '-' in self.mac:
            return False
        if regex_mac.match(self.mac.lower()) is None:
            return False
        if ':' in self.mac:
            self.mac = self.mac.replace(':', '')
        if '-' in self.mac:
            self.mac = self.mac.replace('-', '')
        if len(self.mac) != 12:
            return False
        return True

    def check_broadcast(self):
        if regex_broadcast.match(self.broadcast) is None:
            return False
        return True

    def send(self):
        buf = ''.join(['FFFFFFFFFFFF', self.mac * 20])
        payload = ''
        for i in range(0, len(buf), 2):
            payload = ''.join([payload,struct.pack('B', int(buf[i: i + 2], 16))])
        try:
            self.sock.sendto(payload, (self.broadcast, self.udpport))
        except:
            return False
        return True

   0707010005c998000081b40000000000000000000000015ab217540000686b000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/rcXtremio.py   from __future__ import print_function

import sys
import os
import json

try:
    import ConfigParser
except ImportError:
    import configparser as ConfigParser

import rcExceptions as ex
from rcGlobalEnv import rcEnv, Storage
from rcUtilities import justcall
from converters import convert_size
from rcOptParser import OptParser
from optparse import Option

try:
    import requests
except ImportError:
    raise ex.excInitError("the requests module must be installed")

try:
    requests.packages.urllib3.disable_warnings()
except:
    pass
verify = False

PROG = "nodemgr array"
OPT = Storage({
    "help": Option(
        "-h", "--help", action="store_true", dest="parm_help",
        help="show this help message and exit"),
    "array": Option(
        "-a", "--array", action="store", dest="array_name",
        help="The name of the array, as defined in auth.conf"),
    "name": Option(
        "--name", action="store", dest="name",
        help="The object name"),
    "size": Option(
        "--size", action="store", dest="size",
        help="The disk size, expressed as a size expression like 1g, 100mib, ..."),
    "tags": Option(
        "--tag", action="append", dest="tags",
        help="An object tag. Can be set multiple times."),
    "blocksize": Option(
        "--blocksize", type=int, action="store", dest="blocksize",
        help="The exported disk blocksize in B"),
    "alignment_offset": Option(
        "--alignment-offset", type=int, action="store", dest="alignment_offset",
        help="The alignment offset for Volumes of 512 LB size is between 0 and "
             "7. If omitted, the offset value is 0.  Volumes of logical block "
             "size 4096 must not be defined with an offset."),
    "small_io_alerts": Option(
        "--small-io-alerts", action="store", dest="small_io_alerts",
        choices=["enabled", "disabled"],
        help="Enable or disable small input/output Alerts"),
    "unaligned_io_alerts": Option(
        "--unaligned-io-alerts", action="store", dest="unaligned_io_alerts",
        choices=["enabled", "disabled"],
        help="Enable or disable unaligned input/output Alerts"),
    "vaai_tp_alerts": Option(
        "--vaai-tp-alerts", action="store", dest="vaai_tp_alerts",
        choices=["enabled", "disabled"],
        help="Enable or disable VAAI TP Alerts"),
    "access": Option(
        "--access", action="store", dest="access",
        choices=["no_access", "read_access", "write_access"],
        help="A Volume is created with write access rights."
             "Volumes can be modified after being created and"
             "have their access levels' changed."),
    "naa": Option(
        "--naa", action="store", dest="naa",
        help="The volume naa identifier"),
    "mappings": Option(
        "--mappings", action="append", dest="mappings",
        help="A <hba_id>:<tgt_id>,<tgt_id>,... mapping used in add map in replacement of --targetgroup and --initiatorgroup. Can be specified multiple times."),
    "initiators": Option(
        "--initiators", action="append", dest="initiators",
        help="An initiator id. Can be specified multiple times."),
    "initiator": Option(
            "--initiator", action="store", dest="initiator",
            help="An initiator id"),
    "targets": Option(
        "--targets", action="append", dest="targets",
        help="A target name to export the disk through. Can be set multiple times."),
    "target": Option(
        "--target", action="store", dest="target",
        help="A target name or id"),
    "targetgroup": Option(
        "--targetgroup", action="store", dest="targetgroup",
        help="A target group name or id"),
    "initiatorgroup": Option(
        "--initiatorgroup", action="store", dest="initiatorgroup",
        help="The initiator group id or name"),
    "volume": Option(
        "--volume", action="store", dest="volume",
        help="A volume name or id"),
    "lun": Option(
        "--lun", action="store", type=int, dest="lun",
        help="Unique LUN identification, exposing the Volume to"
             "the host"),
    "mapping": Option(
        "--mapping", action="store", type=int, dest="mapping",
        help="A lun mapping index"),
})

GLOBAL_OPTS = [
    OPT.array,
]

DEPRECATED_ACTIONS = []

ACTIONS = {
    "Add actions": {
        "add_disk": {
            "msg": "Add a volume",
            "options": [
                OPT.name,
                OPT.size,
                OPT.blocksize,
                OPT.tags,
                OPT.alignment_offset,
                OPT.small_io_alerts,
                OPT.unaligned_io_alerts,
                OPT.vaai_tp_alerts,
                OPT.access,
                OPT.mappings,
            ],
        },
        "add_map": {
            "msg": "Map a volume to an initiator group and target group",
            "options": [
                OPT.volume,
                OPT.mappings,
                OPT.initiatorgroup,
                OPT.targetgroup,
            ],
        },
        "del_disk": {
            "msg": "Delete a volume",
            "options": [
                OPT.volume,
            ],
        },
        "del_map": {
            "msg": "Unmap a volume from an initiator group and target group",
            "options": [
                OPT.mapping,
                OPT.volume,
                OPT.initiatorgroup,
                OPT.targetgroup,
            ],
        },
        "resize_disk": {
            "msg": "Resize a volume",
            "options": [
                OPT.volume,
                OPT.size,
            ],
        },
    },
    "Low-level actions": {
        "list_initiators": {
            "msg": "List configured initiators",
            "options": [
                OPT.initiator,
            ],
        },
        "list_initiator_groups": {
            "msg": "List configured initiator groups",
            "options": [
                OPT.initiatorgroup,
            ],
        },
        "list_initiators_connectivity": {
            "msg": "List configured initiator groups",
        },
        "list_mappings": {
            "msg": "List configured mappings",
            "options": [
                OPT.mapping,
                OPT.volume,
            ],
        },
        "list_targets": {
            "msg": "List configured targets",
            "options": [
                OPT.target,
            ],
        },
        "list_target_groups": {
            "msg": "List configured target groups",
            "options": [
                OPT.targetgroup,
            ],
        },
        "list_volumes": {
            "msg": "List configured volumes",
            "options": [
                OPT.volume,
            ],
        },
    },
}

class Arrays(object):
    arrays = []

    def __init__(self, objects=[]):
        self.objects = objects
        if len(objects) > 0:
            self.filtering = True
        else:
            self.filtering = False
        cf = rcEnv.paths.authconf
        if not os.path.exists(cf):
            return
        conf = ConfigParser.RawConfigParser()
        conf.read(cf)
        m = []
        for s in conf.sections():
            try:
                stype = conf.get(s, 'type')
            except:
                continue
            if stype != "xtremio":
                continue
            try:
                name = s
                api = conf.get(s, 'api')
                username = conf.get(s, 'username')
                password = conf.get(s, 'password')
                m += [(name, api, username, password)]
            except:
                print("error parsing section", s)
                pass
        del(conf)
        done = []
        for name, api, username, password in m:
            if self.filtering and name not in self.objects:
                continue
            if name in done:
                continue
            self.arrays.append(Array(name, api, username, password))
            done.append(name)

    def __iter__(self):
        for array in self.arrays:
            yield(array)

    def get_array(self, name):
        for array in self.arrays:
            if array.name == name:
                return array
        return None

class Array(object):
    def __init__(self, name, api, username, password):
        self.name = name
        self.api = api
        self.username = username
        self.password = password
        self.auth = (username, password)
        self.keys = [
            'clusters_details',
            'volumes_details',
            'targets_details',
        ]

        self.tg_portname = {}
        self.ig_portname = {}

    def convert_ids(self, data):
        if data is None:
            return data
        for key in data:
            if not isinstance(key, str):
                continue
            if not key.endswith("-id"):
                continue
            try:
                data[key] = int(data[key])
            except ValueError:
                pass
        return data

    def delete(self, uri, params=None, data=None):
        if params is None:
            params = {}
        params["cluster-name"] = self.name
        headers = {"Cache-Control": "no-cache"}
        data = self.convert_ids(data)
        if not uri.startswith("http"):
            uri = self.api + uri
        response = requests.delete(uri, params=params, data=json.dumps(data),
                                   auth=self.auth, verify=verify,
                                   headers=headers)
        if response.status_code == 200:
            return
        raise ex.excError(response.content)

    def put(self, uri, params=None, data=None):
        if data is None:
            data = {}
        data["cluster-id"] = self.name
        headers = {"Cache-Control": "no-cache"}
        data = self.convert_ids(data)
        if not uri.startswith("http"):
            uri = self.api + uri
        response = requests.put(uri, params=params, data=json.dumps(data), auth=self.auth,
                                verify=verify, headers=headers)
        if response.status_code == 200:
            return
        raise ex.excError(response.content)

    def post(self, uri, params=None, data=None):
        if data is None:
            data = {}
        data["cluster-id"] = self.name
        headers = {"Cache-Control": "no-cache"}
        data = self.convert_ids(data)
        if not uri.startswith("http"):
            uri = self.api + uri
        response = requests.post(uri, params=params, data=json.dumps(data), auth=self.auth,
                                 verify=verify, headers=headers)
        ret = json.loads(response.content)
        if response.status_code == 201:
            return self.get(ret["links"][0]["href"])
        raise ex.excError(response.content)

    def get(self, uri, params=None):
        if params is None:
            params = {}
        params["cluster-name"] = self.name
        headers = {"Cache-Control": "no-cache"}
        if not uri.startswith("http"):
            uri = self.api + uri
        r = requests.get(uri, params=params, auth=self.auth, verify=verify)
        return json.loads(r.content)

    def get_clusters_details(self):
        data = self.get("/clusters", params={"full": 1})
        return json.dumps(data["clusters"], indent=8)

    def get_targets_details(self):
        data = self.get("/targets", params={"full": 1})
        return json.dumps(data["targets"], indent=8)

    def get_volumes_details(self):
        data = self.get("/volumes", params={"full": 1})
        return json.dumps(data["volumes"], indent=8)

    def add_disk(self, name=None, size=None, blocksize=None, tags=None,
                 access=None, vaai_tp_alerts=None,
                 small_io_alerts=None, unaligned_io_alerts=None,
                 alignment_offset=None, mappings=None, **kwargs):
        if name is None:
            raise ex.excError("--name is mandatory")
        if size == 0 or size is None:
            raise ex.excError("--size is mandatory")
        d = {
            "vol-name": name,
            "vol-size": str(convert_size(size, _to="MB"))+"M",
        }
        if blocksize is not None:
            d["lb-size"] = blocksize
        if small_io_alerts is not None:
            d["small-io-alerts"] = small_io_alerts
        if unaligned_io_alerts is not None:
            d["unaligned-io-alerts"] = unaligned_io_alerts
        if access is not None:
            d["vol-access"] = access
        if vaai_tp_alerts is not None:
            d["vaai-tp-alerts"] = vaai_tp_alerts
        if alignment_offset is not None:
            d["alignment-offset"] = alignment_offset
        self.post("/volumes", data=d)
        driver_data = {}
        if mappings:
            mappings_data = self.add_map(volume=name, mappings=mappings)
        driver_data["volume"] = self.get_volumes(volume=name)["content"]
        driver_data["mappings"] = mappings_data.values()
        results = {
            "driver_data": driver_data,
            "disk_id": driver_data["volume"]["naa-name"],
            "disk_devid": driver_data["volume"]["index"],
            "mappings": {},
        }
        for ig, tg in list(mappings_data.keys()):
            if ig not in self.ig_portname:
                continue
            for hba_id in self.ig_portname[ig]:
                if tg not in self.tg_portname:
                    continue
                for tgt_id in self.tg_portname[tg]:
                    results["mappings"][hba_id+":"+tgt_id] = {
                        "hba_id": hba_id,
                        "tgt_id": tgt_id,
                        "lun": mappings_data[(ig, tg)]["lun"],
                    }
        self.push_diskinfo(results, name, size)
        return results

    def resize_disk(self, volume=None, size=None, **kwargs):
        if volume is None:
            raise ex.excError("--volume is mandatory")
        if volume is "":
            raise ex.excError("--volume can not be empty")
        if size == 0 or size is None:
            raise ex.excError("--size is mandatory")
        if size.startswith("+"):
            incr = convert_size(size.lstrip("+"), _to="KB")
            data = self.get_volumes(volume=volume)
            current_size = int(data["content"]["vol-size"])
            size = str(current_size + incr)+"K"
        d = {
            "vol-size": str(convert_size(size, _to="MB"))+"M",
        }
        uri = "/volumes"
        params = {}
        if volume is not None:
            try:
                int(volume)
                uri += "/"+str(volume)
            except ValueError:
                params["name"] = volume
        self.put(uri, params=params, data=d)
        ret = self.get_volumes(volume=volume)
        return ret

    def get_volume_mappings(self, volume=None, **kwargs):
        params = {"full": 1}
        uri = "/lun-maps"
        if volume is None:
            raise ex.excError("--volume is mandatory")
        data = self.get_volumes(volume=volume)
        vol_name = data["content"]["name"]
        params["filter"] = "vol-name:eq:"+vol_name
        data = self.get(uri, params=params)
        return data

    def del_volume_mappings(self, volume=None, **kwargs):
        data = self.get_volume_mappings(volume=volume)
        for mapping in data["lun-maps"]:
            self.del_map(mapping=mapping["index"])

    def del_disk(self, volume=None, **kwargs):
        if volume is None:
            raise ex.excError("--volume is mandatory")
        if volume == "":
            raise ex.excError("volume can not be empty")
        data = self.get_volumes(volume=volume)
        if "content" not in data:
            raise ex.excError("volume %s does not exist" % volume)
        disk_id = data["content"]["naa-name"]
        self.del_volume_mappings(volume=volume)
        params = {}
        uri = "/volumes"
        try:
            int(volume)
            uri += "/"+str(volume)
        except ValueError:
            params["name"] = volume
        ret = self.delete(uri, params=params)
        self.del_diskinfo(disk_id)
        return ret

    def convert_hba_id(self, hba_id):
        hba_id = hba_id[0:2] + ":" + \
                 hba_id[2:4] + ":" + \
                 hba_id[4:6] + ":" + \
                 hba_id[6:8] + ":" + \
                 hba_id[8:10] + ":" + \
                 hba_id[10:12] + ":" + \
                 hba_id[12:14] + ":" + \
                 hba_id[14:16]
        return hba_id

    def get_hba_initiatorgroup(self, hba_id):
        params = {"full": 1}
        uri = "/initiators"
        hba_id = self.convert_hba_id(hba_id)
        params["filter"] = "port-address:eq:"+hba_id
        data = self.get(uri, params=params)
        if len(data["initiators"]) == 0:
            raise ex.excError("no initiator found with port-address=%s" % hba_id)
        if len(data["initiators"][0]["ig-id"]) == 0:
            raise ex.excError("initiator %s found in no initiatorgroup" % hba_id)
        return data["initiators"][0]["ig-id"][-1]

    def get_target_targetgroup(self, hba_id):
        params = {"full": 1}
        uri = "/targets"
        hba_id = self.convert_hba_id(hba_id)
        params["filter"] = "port-address:eq:"+hba_id
        data = self.get(uri, params=params)
        if len(data["targets"]) == 0:
            raise ex.excError("no target found with port-address=%s" % hba_id)
        if len(data["targets"][0]["tg-id"]) == 0:
            raise ex.excError("target %s found in no targetgroup" % hba_id)
        return data["targets"][0]["tg-id"][-1]

    def translate_mappings(self, mappings):
        internal_mappings = {}
        for mapping in mappings:
            elements = mapping.split(":")
            hba_id = elements[0]
            targets = elements[-1].split(",")
            ig = self.get_hba_initiatorgroup(hba_id)
            if ig not in self.ig_portname:
                self.ig_portname[ig] = []
            self.ig_portname[ig].append(hba_id)
            internal_mappings[ig] = set()
            for target in targets:
                tg = self.get_target_targetgroup(target)
                if tg not in self.tg_portname:
                    self.tg_portname[tg] = []
                self.tg_portname[tg].append(target)
                internal_mappings[ig].add(tg)
        return internal_mappings

    def add_map(self, volume=None, mappings=None, initiatorgroup=None, targetgroup=None,
                lun=None, **kwargs):
        if volume is None:
            raise ex.excError("--volume is mandatory")
        results = {}
        if mappings is not None and initiatorgroup is None:
            internal_mappings = self.translate_mappings(mappings)
            for ig, tgs in internal_mappings.items():
                for tg in tgs:
                    map_data = self._add_map(volume=volume, initiatorgroup=ig, targetgroup=tg, lun=lun, **kwargs)
                    results[(ig, tg)] = map_data
        else:
            map_data = self._add_map(volume=volume, initiatorgroup=initiatorgroup, targetgroup=targetgroup, lun=lun, **kwargs)
            results[(initiatorgroup, targetgroup)] = map_data
        return results

    def _add_map(self, volume=None, initiatorgroup=None, targetgroup=None,
                 lun=None, **kwargs):
        if initiatorgroup is None:
            raise ex.excError("--initiatorgroup is mandatory")
        d = {
            "vol-id": volume,
            "ig-id": initiatorgroup,
        }
        if targetgroup is not None:
            d["tg-id"] = targetgroup
        if lun is not None:
            d["lun"] = lun
        ret = self.post("/lun-maps", data=d)
        return ret["content"]

    def del_map(self, mapping=None, **kwargs):
        if mapping is None:
            raise ex.excError("--mapping is mandatory")
        if mapping == "":
            raise ex.excError("mapping can not be empty")
        params = {}
        uri = "/lun-maps"
        if mapping is not None:
            try:
                int(mapping)
                uri += "/"+str(mapping)
            except ValueError:
                params["name"] = mapping
        return self.delete(uri, params=params)

    def list_target_groups(self, targetgroup=None, **kwargs):
        params = {"full": 1}
        uri = "/target-groups"
        if targetgroup is not None:
            try:
                int(targetgroup)
                uri += "/"+str(targetgroup)
            except ValueError:
                params["name"] = targetgroup
        data = self.get(uri, params=params)
        if "target-groups" in data:
            print(json.dumps(data["target-groups"], indent=8))
        elif "content" in data:
            print(json.dumps(data["content"], indent=8))
        else:
            print(json.dumps(data, indent=8))

    def get_initiators(self, initiator=None, **kwargs):
        params = {"full": 1}
        uri = "/initiators"
        if initiator is not None:
            try:
                int(initiator)
                uri += "/"+str(initiator)
            except ValueError:
                params["name"] = initiator
        data = self.get(uri, params=params)
        return data

    def list_initiators(self, initiator=None, **kwargs):
        data = self.get_initiators(initiator=initiator, **kwargs)
        if "initiators" in data:
            print(json.dumps(data["initiators"], indent=8))
        elif "content" in data:
            print(json.dumps(data["content"], indent=8))
        else:
            print(json.dumps(data, indent=8))

    def list_initiator_groups(self, initiatorgroup=None, **kwargs):
        params = {"full": 1}
        uri = "/initiator-groups"
        if initiatorgroup is not None:
            try:
                int(initiatorgroup)
                uri += "/"+str(initiatorgroup)
            except ValueError:
                params["name"] = initiatorgroup
        data = self.get(uri, params=params)
        if "initiator-groups" in data:
            print(json.dumps(data["initiator-groups"], indent=8))
        elif "content" in data:
            print(json.dumps(data["content"], indent=8))
        else:
            print(json.dumps(data, indent=8))

    def list_initiators_connectivity(self, **kwargs):
        params = {}
        uri = "/initiators-connectivity"
        data = self.get(uri, params=params)
        if "content" in data:
            print(json.dumps(data["content"], indent=8))
        else:
            print(json.dumps(data, indent=8))

    def list_targets(self, target=None, **kwargs):
        params = {"full": 1}
        uri = "/targets"
        if target is not None:
            try:
                int(target)
                uri += "/"+str(target)
            except ValueError:
                params["name"] = target
        data = self.get(uri, params=params)
        if "targets" in data:
            print(json.dumps(data["targets"], indent=8))
        elif "content" in data:
            print(json.dumps(data["content"], indent=8))
        else:
            print(json.dumps(data, indent=8))

    def list_mappings(self, mapping=None, volume=None, **kwargs):
        params = {"full": 1}
        uri = "/lun-maps"
        if mapping is not None:
            try:
                int(mapping)
                uri += "/"+str(mapping)
            except ValueError:
                params["name"] = mapping
        if volume is not None:
            try:
                int(volume)
                params["filter"] = "vol-index:eq:"+str(volume)
                print(params)
            except ValueError:
                params["filter"] = "vol-name:eq:"+volume
        data = self.get(uri, params=params)
        if "targets" in data:
            print(json.dumps(data["lun-maps"], indent=8))
        elif "content" in data:
            print(json.dumps(data["content"], indent=8))
        else:
            print(json.dumps(data, indent=8))

    def get_volumes(self, volume=None, **kwargs):
        params = {"full": 1}
        uri = "/volumes"
        if volume is not None:
            try:
                int(volume)
                uri += "/"+str(volume)
            except ValueError:
                params["name"] = volume
        data = self.get(uri, params=params)
        return data

    def list_volumes(self, volume=None, **kwargs):
        data = self.get_volumes(volume=volume, **kwargs)
        if "volumes" in data:
            print(json.dumps(data["volumes"], indent=8))
        elif "content" in data:
            print(json.dumps(data["content"], indent=8))
        else:
            print(json.dumps(data, indent=8))

    def del_diskinfo(self, disk_id):
        if disk_id in (None, ""):
            return
        if self.node is None:
            return
        try:
            ret = self.node.collector_rest_delete("/disks/%s" % disk_id)
        except Exception as exc:
            raise ex.excError(str(exc))
        if "error" in ret:
            self.log.error("failed to delete the disk object in the collector: %s", ret["error"])
        return ret

    def push_diskinfo(self, data, name, size):
        if self.node is None:
            return
        if data["disk_id"] in (None, ""):
            data["disk_id"] = self.name+"."+str(data["driver_info"]["volume"]["index"])
        try:
            ret = self.node.collector_rest_post("/disks", {
                "disk_id": data["disk_id"],
                "disk_devid": data["disk_devid"],
                "disk_name": name,
                "disk_size": convert_size(size, _to="MB"),
                "disk_alloc": 0,
                "disk_arrayid": self.name,
                "disk_group": "default",
            })
        except Exception as exc:
            raise ex.excError(str(exc))
        if "error" in data:
            raise ex.excError(ret["error"])
        return ret

def do_action(action, array_name=None, node=None, **kwargs):
    o = Arrays()
    array = o.get_array(array_name)
    if array is None:
        raise ex.excError("array %s not found" % array_name)
    if not hasattr(array, action):
        raise ex.excError("not implemented")
    array.node = node
    ret = getattr(array, action)(**kwargs)
    if ret is not None:
        print(json.dumps(ret, indent=4))

def main(argv, node=None):
    parser = OptParser(prog=PROG, options=OPT, actions=ACTIONS,
                       deprecated_actions=DEPRECATED_ACTIONS,
                       global_options=GLOBAL_OPTS)
    options, action = parser.parse_args(argv)
    kwargs = vars(options)
    do_action(action, node=node, **kwargs)

if __name__ == "__main__":
    try:
        main(sys.argv)
        ret = 0
    except ex.excError as exc:
        print(exc, file=sys.stderr)
        ret = 1
    sys.exit(ret)


 0707010005c99a000081b40000000000000000000000015ab2175400000c8f000000660000000000000000000000000000002500000000root/usr/share/opensvc/lib/rcZone.py  import os
from rcZfs import Dataset
from rcUtilities import justcall, which

ZONEADM="/usr/sbin/zoneadm"

def is_zone():
    if not which('zonename'):
        return False
    out, err, ret = justcall(['zonename'])
    if ret != 0:
        return False
    if out.strip() == 'global':
        return False
    return True

class Zones(object):
    """Define zones (a.k.a. container) defined on a node"""
    def __init__(self):
        """scan node to get informations about its zones
        """
        self.zone_list = []
        self.zonename2zone = dict()
        self.zonepath2zone = dict()

        cmd = [ZONEADM, 'list', '-cip']
        (out, err, status) = justcall(cmd)
        if status == 0:
            #zoneid:zonename:state:zonepath:uuid:brand:ip-type
            for zoneadm_line in out.split('\n'):
                v = zoneadm_line.split(':')
                if len(v) != 7:
                    continue
                zone = Zone(zoneid=v[0], zonename=v[1], state=v[2],
                            zonepath=v[3], uuid=v[4], brand=v[5], ip_type=v[6])
                self.zonename2zone[zone.zonename] = zone
                self.zonepath2zone[zone.zonepath] = zone
                self.zone_list.append(zone)

    def refresh(self):
        """refresh zones information"""
        self.__init__()

    def zonename_from_zonepath(self, zonepath=None):
        """return zonename associated with zonepath, else return None"""
        if zonepath in self.zonepath2zone:
            return self.zonepath2zone[zonepath]
        else:
            return None

class Zone(object):
    def __init__(self, zoneid=None, zonename=None, state=None, zonepath=None,
                uuid=None, brand=None, ip_type=None ):
        """define Zone object attribute from zoneadm output line
                zoneid:zonename:state:zonepath:uuid:brand:ip-type
        """
        if state is None and zonename is not None:
            self.zonename = zonename
            self.refresh()
        else:
            self.zoneid = zoneid
            self.zonename = zonename
            self.state = state
            self.zonepath = zonepath
            self.uuid = uuid
            self.brand = brand
            self.ip_type = ip_type

    def refresh(self):
        """refresh zone information"""
        cmd = [ZONEADM, '-z', self.zonename, 'list', '-p']
        (out, err, status) = justcall(cmd)
        if status == 0:
            (self.zoneid, self.zonename, self.state, self.zonepath, self.uuid,
                self.brand, self.ip_type ) = out.split('\n')[0].split(':')
        else:
            print("fail to refresh zone informations for zonename", self.zonename)

if __name__ == "__main__":
    zones = Zones()
    print("Detected %s zones on system" % (len(zones.zone_list)))
    for zone in zones.zone_list:
        zonepath = zone.zonepath
        zonename = zone.zonename
        print("zonename=%s zonepath=%s zones.zonepath2zone[%s].zonename=%s" % (
            zonename, zonepath, zonepath, zones.zonepath2zone[zonepath].zonename
            ))
        z = Zone(zonename=zonename)
        print("zone %s : zoneid=%s, zonepath=%s brand=%s" % (zonename, z.zoneid,
                z.zonepath, z.brand))
 0707010005c99b000081b40000000000000000000000015ab2175400003fdd000000660000000000000000000000000000002500000000root/usr/share/opensvc/lib/resApp.py  """
The module defining the App resource and RsetApps resourceset objects.
"""
from subprocess import Popen
from datetime import datetime
import os
import time
import stat
import shlex

from rcUtilities import justcall, which, lazy, is_string, lcall
from converters import convert_boolean
from rcGlobalEnv import rcEnv
from resources import Resource
import rcStatus
import rcExceptions as ex
import lock

def run_as_popen_kwargs(fpath, limits):
    """
    Setup the Popen keyword args to execute <fpath> with the
    privileges demoted to those of the owner of <fpath>.
    """
    if rcEnv.sysname == "Windows":
        return {}
    import pwd
    try:
        fstat = os.stat(fpath)
    except Exception as exc:
        raise ex.excError(str(exc))
    cwd = rcEnv.paths.pathtmp
    user_uid = fstat[stat.ST_UID]
    user_gid = fstat[stat.ST_GID]
    try:
        user_name = pwd.getpwuid(user_uid)[0]
    except KeyError:
        user_name = "unknown"
    try:
        pw_record = pwd.getpwnam(user_name)
        user_name = pw_record.pw_name
        user_home_dir = pw_record.pw_dir
    except KeyError:
        user_home_dir = rcEnv.paths.pathtmp
    env = os.environ.copy()
    env['HOME'] = user_home_dir
    env['LOGNAME'] = user_name
    env['PWD'] = cwd
    env['USER'] = user_name
    return {'preexec_fn': preexec(user_uid, user_gid, limits), 'cwd': cwd, 'env': env}

def preexec(user_uid, user_gid, limits):
    def result():
        set_rlimits(limits)
        demote(user_uid, user_gid)
    return result

def set_rlimits(limits):
    """
    Set the resource limits for the executed command.
    """
    try:
        import resource
    except ImportError:
        return
    for res, val in limits.items():
        rlim = getattr(resource, "RLIMIT_"+res.upper())
        _vs, _vg = resource.getrlimit(rlim)
        resource.setrlimit(rlim, (val, val))

def demote(user_uid, user_gid):
    """
    Return a privilege demotion function to plug as Popen() preexec_fn keyword
    argument, customized for <user_uid> and <user_gid>.
    """
    os.setgid(user_gid)
    os.setuid(user_uid)

class StatusWARN(Exception):
    """
    A class to raise to signal status() to return a "warn" state.
    """
    pass

class StatusNA(Exception):
    """
    A class to raise to signal status() to return a "n/a" state.
    """
    pass

class App(Resource):
    """
    The App resource driver class.
    """

    def __init__(self, rid=None,
                 script=None,
                 start=None,
                 stop=None,
                 check=None,
                 info=None,
                 timeout=None,
                 start_timeout=None,
                 stop_timeout=None,
                 check_timeout=None,
                 info_timeout=None,
                 **kwargs):

        Resource.__init__(self, rid, "app", **kwargs)
        self.script = script
        self.start_seq = start
        self.stop_seq = stop
        self.check_seq = check
        self.info_seq = info
        self.timeout = timeout
        self.start_timeout = start_timeout
        self.stop_timeout = stop_timeout
        self.check_timeout = check_timeout
        self.info_timeout = info_timeout
        if script:
            self.label = os.path.basename(script)
        self.lockfd = None
        try:
            # compat
            self.sort_key = "app#%d" % int(self.start_seq)
        except (TypeError, ValueError):
            pass

    @lazy
    def lockfile(self):
        """
        Lazy init for the resource lock file path property.
        """
        lockfile = os.path.join(rcEnv.paths.pathlock, self.svc.svcname)
        lockfile = ".".join((lockfile, self.rid))
        return lockfile

    def validate_on_action(self, cmd):
        """
        Do sanity checks on the resource parameters before running an action.
        """
        cmd = self.validate_script_path(cmd)
        self.validate_script_exec(cmd)
        return cmd

    def validate_script_exec(self, cmd):
        """
        Invalidate the script if the file is not executable or not found.
        """
        if cmd is None:
            return
        if which(cmd[0]) is None:
            self.status_log("%s is not executable" % cmd[0])
            self.set_executable(cmd)

    def validate_script_path(self, cmd):
        """
        Converts the script path to a realpath.
        Invalidate the script if not found.
        If the script is specified as a basename, consider it is to be found
        in the <pathetc>/<svcname>.d directory.
        """
        if cmd is None:
            return
        if not cmd[0].startswith('/'):
            cmd[0] = os.path.join(self.svc.paths.initd, cmd[0])
        if os.path.exists(cmd[0]):
            cmd[0] = os.path.realpath(cmd[0])
            return cmd
        raise ex.excError("%s does not exist" % cmd[0])

    def is_up(self):
        """
        Return 0 if the app resource is up.
        """
        if self.pg_frozen():
            raise StatusNA()
        if self.check_seq is False:
            self.status_log("check is not set", "info")
            raise StatusNA()
        try:
            cmd = self.get_cmd("check", "status")
        except ex.excAbortAction:
            raise StatusNA()
        except ex.excError as exc:
            self.status_log(str(exc), "warn")
            raise StatusNA()
        ret = self.run("status", cmd, dedicated_log=False)
        return ret

    def _info(self):
        """
        Contribute app resource standard and script-provided key/val pairs
        to the service's resinfo.
        """
        import re
        keyvals = [
            ["script", self.script if self.script else ""],
            ["start", str(self.start_seq) if self.start_seq else ""],
            ["stop", str(self.stop_seq) if self.stop_seq else ""],
            ["check", str(self.check_seq) if self.check_seq else ""],
            ["info", str(self.info_seq) if self.info_seq else ""],
            ["timeout", str(self.timeout) if self.timeout else ""],
            ["start_timeout", str(self.start_timeout) if self.start_timeout else ""],
            ["stop_timeout", str(self.stop_timeout) if self.stop_timeout else ""],
            ["check_timeout", str(self.check_timeout) if self.check_timeout else ""],
            ["info_timeout", str(self.info_timeout) if self.info_timeout else ""],
        ]
        try:
            cmd = self.get_cmd("info")
        except (ex.excAbortAction, AttributeError):
            return keyvals

        buff = self.run('info', cmd, dedicated_log=False, return_out=True)
        if not is_string(buff) or len(buff) == 0:
            keyvals.append(["Error", "info not implemented in launcher"])
            return keyvals
        for line in buff.splitlines():
            if re.match("\w+:\s*.*$", line) is None:
                continue
            if len(line) == 0:
                continue
            elements = line.split(":")
            if len(elements) < 2:
                continue
            keyvals.append([elements[0].strip(), ":".join(elements[1:]).strip()])
        return keyvals

    def get_cmd(self, action, script_arg=None):
        key = action + "_seq"
        val = getattr(self, key)
        if val is False:
            raise ex.excAbortAction()
        try:
            int(val)
            cmd = [self.script, script_arg if script_arg else action]
        except (TypeError, ValueError):
            try:
                val = convert_boolean(val)
                if val is False:
                    raise ex.excAbortAction()
                cmd = [self.script, script_arg if script_arg else action]
            except:
                cmd = shlex.split(val)
        cmd = self.validate_on_action(cmd)
        return cmd

    def start(self):
        """
        Start the resource.
        """
        self.create_pg()

        try:
            cmd = self.get_cmd("start")
        except ex.excAbortAction:
            return

        try:
            status = self.is_up()
        except:
            status = 1

        if status == 0:
            self.log.info("%s is already started", self.label)
            return

        ret = self.run("start", cmd)
        if ret != 0:
            raise ex.excError()
        self.can_rollback = True

    def stop(self):
        """
        Stop the resource.
        """
        try:
            cmd = self.get_cmd("stop")
        except ex.excAbortAction:
            return
        except ex.excError as exc:
            if "does not exist" in str(exc):
                return
            raise

        status = self.status()
        if status == rcStatus.DOWN:
            self.log.info("%s is already stopped", self.label)
            return

        try:
            self.run("stop", cmd)
        except Exception as exc:
            self.log.warning(exc)

    def unlock(self):
        """
        Release the app action lock.
        """
        self.log.debug("release app lock")
        lock.unlock(self.lockfd)
        try:
            os.unlink(self.lockfile)
        except OSError:
            pass
        self.lockfd = None

    def lock(self, action=None, timeout=0, delay=1):
        """
        Acquire the app action lock.
        """
        if self.lockfd is not None:
            return

        details = "(timeout %d, delay %d, action %s, lockfile %s)" % \
                  (timeout, delay, action, self.lockfile)
        self.log.debug("acquire app lock %s", details)
        try:
            lockfd = lock.lock(
                timeout=timeout,
                delay=delay,
                lockfile=self.lockfile,
                intent=action
            )
        except lock.LockTimeout as exc:
            raise ex.excError("timed out waiting for lock %s: %s" % (details, str(exc)))
        except lock.LockNoLockFile:
            raise ex.excError("lock_nowait: set the 'lockfile' param %s" % details)
        except lock.LockCreateError:
            raise ex.excError("can not create lock file %s" % details)
        except lock.LockAcquire as exc:
            raise ex.excError("another action is currently running %s: %s" % (details, str(exc)))
        except ex.excSignal:
            raise ex.excError("interrupted by signal %s" % details)
        except Exception as exc:
            self.save_exc()
            raise ex.excError("unexpected locking error %s: %s" % (details, str(exc)))

        if lockfd is not None:
            self.lockfd = lockfd

    def _status(self, verbose=False):
        """
        Return the resource status.
        """
        n_ref_res = len(self.svc.get_resources(['fs', 'ip', 'container', 'share', 'disk']))
        status = self.svc.group_status(excluded_groups=set([
            "sync",
            "app",
            "disk.scsireserv",
            "disk.drbd",
            "hb"
        ]))
        if n_ref_res > 0 and str(status["avail"]) not in ("up", "n/a"):
            self.log.debug("abort resApp status because needed resources avail status is %s", status["avail"])
            self.status_log("not evaluated (instance not up)", "info")
            return rcStatus.NA

        try:
            ret = self.is_up()
        except StatusWARN:
            return rcStatus.WARN
        except StatusNA:
            return rcStatus.NA
        except ex.excError as exc:
            msg = str(exc)
            if "intent '" in msg:
                action = msg.split("intent '")[-1].split("'")[0]
                self.status_log("%s in progress" % action, "info")
            self.log.debug("resource status forced to n/a: an action is running")
            return rcStatus.NA

        if ret == 0:
            return rcStatus.UP
        elif ret == 1:
            return rcStatus.DOWN

        self.status_log("check reports errors (%d)" % ret)
        return rcStatus.WARN

    def set_executable(self, cmd):
        """
        Switch the script file execution bit to on.
        """
        if not os.path.exists(cmd[0]):
            return
        self.vcall(['chmod', '+x', cmd[0]])

    def run(self, action, cmd, dedicated_log=True, return_out=False):
        """
        Acquire the app resource lock, run the action and release for info, start
        and stop actions.
        Or acquire-release the app resource lock and run status.
        """
        self.lock(action)
        if action == "status":
            self.unlock()
        try:
            return self._run(action, cmd, dedicated_log=dedicated_log, return_out=return_out)
        finally:
            self.unlock()

    def _run(self, action, cmd, dedicated_log=True, return_out=False):
        """
        Do script validations, run the command associated with the action and
        catch errors.
        """
        try:
            return self._run_cmd(action, cmd, dedicated_log=dedicated_log, return_out=return_out)
        except OSError as exc:
            if exc.errno == 8:
                if not return_out and not dedicated_log:
                    self.status_log("exec format error")
                    raise StatusWARN()
                else:
                    self.log.error("execution error (Exec format error)")
            elif exc.errno == 13:
                if not return_out and not dedicated_log:
                    self.status_log("permission denied")
                    raise StatusWARN()
                else:
                    self.log.error("execution error (Permission Denied)")
            else:
                self.svc.save_exc()
            return 1
        except:
            self.svc.save_exc()
            return 1

    def _run_cmd(self, action, cmd, dedicated_log=True, return_out=False):
        """
        Switch between buffered outputs or polled execution.
        Return stdout if <return_out>, else return the returncode.
        """
        if dedicated_log:
            return self._run_cmd_dedicated_log(action, cmd)
        elif return_out:
            out, err, ret = justcall(cmd)
            if ret != 0:
                return "Error: info not implemented in launcher"
            return out
        else:
            out, err, ret = justcall(cmd)
            self.log.debug("%s returned out=[%s], err=[%s], ret=[%d]", cmd, out, err, ret)
            return ret

    @lazy
    def limits(self):
        data = {}
        try:
            import resource
        except ImportError:
            return data
        for key in ("as", "cpu", "core", "data", "fsize", "memlock", "nofile", "nproc", "rss", "stack", "vmem"):
            try:
                data[key] = self.conf_get("limit_"+key)
            except ex.OptNotFound:
                continue
            rlim = getattr(resource, "RLIMIT_"+key.upper())
            _vs, _vg = resource.getrlimit(rlim)
            if data[key] > _vs:
                if data[key] > _vg:
                    _vg = data[key]
                resource.setrlimit(rlim, (data[key], _vg))
                _vs, _vg = resource.getrlimit(rlim)
            self.log.info("limit %s = %s", key, data[key])
        return data

    def get_timeout(self, action):
        action_timeout = getattr(self, action+"_timeout")
        if action_timeout is not None:
            return action_timeout
        return self.timeout

    def _run_cmd_dedicated_log(self, action, cmd):
        """
        Poll stdout and stderr to log as soon as new lines are available.
        """
        kwargs = {
            'stdin': None,
            'timeout': self.get_timeout(action),
            'logger': self.log,
        }
        try:
            kwargs.update(run_as_popen_kwargs(cmd[0], self.limits))
        except ValueError as exc:
            self.log.error("%s", exc)
            return 1
        user = kwargs.get("env").get("LOGNAME")
        self.log.info('exec %s as user %s', ' '.join(cmd), user)
        now = datetime.now()
        try:
            ret = lcall(cmd, **kwargs)
        except (KeyboardInterrupt, ex.excSignal):
            _len = datetime.now() - now
            self.log.error('%s interrupted after %s - ret %d',
                           action, _len, 1)
            return 1
        _len = datetime.now() - now
        msg = '%s done in %s - ret %d' % (action, _len, ret)
        if ret == 0:
            self.log.info(msg)
        else:
            self.log.error(msg)
        return ret

   0707010005c99c000081b40000000000000000000000015ab2175400000ca9000000660000000000000000000000000000002700000000root/usr/share/opensvc/lib/resAppVm.py    from subprocess import *
import os
import glob

from rcGlobalEnv import rcEnv
from rcUtilities import qcall, justcall
import resApp
import rcStatus
import rcExceptions as ex

class Apps(resApp.App):
    app_d = os.path.join(os.sep, 'svc', 'etc', 'init.d')

    def set_perms(self, rc):
        (ret, out, err) = self.call(self.prefix+['/usr/bin/find',
                                            self.app_d,
                                            '-name', os.path.basename(rc),
                                            '-a', '-user', 'root',
                                            '-a', '-group', 'root'])
        if len(out) == 0 or rc != out.split()[0]:
            self.vcall(self.prefix+['chown', 'root:root', rc])
        (ret, out, err) = self.call(self.prefix+['test', '-x', rc])
        if ret != 0:
            self.vcall(self.prefix+['chmod', '+x', rc])

    def check_reachable(self, container):
        cmd = self.prefix + ['/bin/pwd']
        ret = qcall(cmd)
        if ret != 0:
            return False
        return True

    def checks(self, verbose=False):
        container = self.svc.resources_by_id["container"]
        if container.guestos == 'Windows':
            raise ex.excNotAvailable
        if container.status(refresh=True) != rcStatus.UP:
            self.log.debug("abort resApp action because container status is %s" % rcStatus.Status(container.status()))
            self.status_log("container is %s" % rcStatus.Status(container.status()))
            raise ex.excNotAvailable
        if not self.check_reachable(container):
            self.log.debug("abort resApp action because container is unreachable")
            self.status_log("container is unreachable")
            return False
        cmd = self.prefix + ['test', '-d', self.app_d]
        ret = qcall(cmd)
        if ret == 0:
            return True
        cmd = self.prefix + ['/bin/mkdir', '-p', self.app_d]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            return False
        return True

    def stop_checks(self):
        return self.checks()

    def start_checks(self):
        return self.checks()

    def status_checks(self, verbose=False):
        return self.checks(verbose=verbose)

    def sorted_app_list(self, pattern):
        cmd = self.prefix + ['/usr/bin/find', self.app_d, '-name', pattern.replace('*', '\*')]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE, close_fds=True)
        buff = p.communicate()
        if p.returncode != 0:
            self.log.debug("failed to fetch container startup scripts list")
            return []

        l = buff[0].split('\n')

        # most unix find commands don't support maxdepth.
        # discard manually the startup scripts found in subdirs of app_d
        n = self.app_d.count("/")
        if not self.app_d.endswith("/"):
            n += 1
        l = [e for e in l if e.count("/") == n]

        return sorted(l)

    def app_exist(self, name):
        """ verify app_exists inside Vm
        """
        (out, err, ret) = justcall (self.prefix + ['/bin/ls', '-Ld', name ])
        if ret == 0:
            return True
        else:
            return False

if __name__ == "__main__":
    for c in (Apps,) :
        help(c)
   0707010005c99d000081b40000000000000000000000015ab217540000208c000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/resContainer.py    import rcStatus
import resources as Res
import time
import rcExceptions as ex
from rcUtilities import justcall, getaddr, lazy
from rcGlobalEnv import rcEnv

class Container(Res.Resource):
    """
    The container base class.
    """
    startup_timeout = 600
    shutdown_timeout = 60

    def __init__(self,
                 rid,
                 name,
                 guestos=None,
                 type=None,
                 osvc_root_path=None,
                 **kwargs):
        Res.Resource.__init__(self,
                              rid=rid,
                              type=type,
                              **kwargs)
        self.osvc_root_path = osvc_root_path
        self.sshbin = '/usr/bin/ssh'
        self.name = name
        try:
            self.label = name
        except AttributeError:
            # label is a lazy prop of the child class
            pass
        self.guestos = guestos
        if guestos is not None:
            self.guestos = guestos.lower()
        if self.guestos != "windows":
            self.runmethod = rcEnv.rsh.split() + [name]
        self.booted = False

    def _info(self):
        """
        Contribute resource key/val pairs to the service's resinfo.
        """
        data = [
            ["name", self.name],
            ["guestos", self.guestos],
        ]
        return data

    @lazy
    def vm_hostname(self):
        try:
            hostname = self.svc.conf_get(self.rid, "hostname")
        except ex.OptNotFound:
            hostname = self.name
        return hostname

    def getaddr(self, cache_fallback=False):
        if hasattr(self, 'addr'):
            return
        if self.type == "container.docker":
            return
        try:
            self.log.debug("resolving %s" % self.vm_hostname)
            self.addr = getaddr(self.vm_hostname, cache_fallback=cache_fallback, log=self.log)
        except Exception as e:
            if not self.is_disabled():
                raise ex.excError("could not resolve name %s: %s" % (self.vm_hostname, str(e)))

    def __str__(self):
        return "%s name=%s" % (Res.Resource.__str__(self), self.name)

    def operational(self):
        if self.guestos == "windows":
            """ Windows has no sshd.
            """
            return True
        timeout = 1
        if 'ssh' in self.runmethod[0]:
            cmd = [ self.sshbin, '-o', 'StrictHostKeyChecking=no',
                                 '-o', 'ForwardX11=no',
                                 '-o', 'BatchMode=yes',
                                 '-n',
                                 '-o', 'ConnectTimeout='+repr(timeout),
                                  self.name, 'pwd']
        else:
            cmd = self.runmethod + ['pwd']
        import subprocess
        out, err, ret = justcall(cmd, stdin=self.svc.node.devnull)
        if ret == 0:
            return True
        return False

    def wait_for_startup(self):
        self.log.info("wait for up status")
        self.wait_for_fn(self.is_up, self.startup_timeout, 2)
        if hasattr(self, 'ping'):
            self.log.info("wait for container ping")
            self.wait_for_fn(self.ping, self.startup_timeout, 2)
        self.log.info("wait for container operational")
        self.wait_for_fn(self.operational, self.startup_timeout, 2)

    def wait_for_shutdown(self):
        self.log.info("wait for down status")
        for tick in range(self.shutdown_timeout):
            if self.is_down():
                return
            time.sleep(1)
        self.log.error("Waited too long for shutdown")
        raise ex.excError

    def install_drp_flag(self):
        print("TODO: install_drp_flag()")

    def where_up(self):
        """ returns None if the vm is not found running anywhere
            or returns the nodename where the vm is found running
        """
        if self.is_up():
            return rcEnv.nodename
        if not hasattr(self, "is_up_on"):
            # to implement in Container child class
            return
        if rcEnv.nodename in self.svc.nodes:
            nodes = self.svc.nodes - set([rcEnv.nodename])
        elif rcEnv.nodename in self.svc.drpnodes:
            nodes = self.svc.drpnodes - set([rcEnv.nodename])
        else:
            nodes = []
        if len(nodes) == 0:
            return
        for node in nodes:
            if self.is_up_on(node):
                return node
        return

    def abort_start_ping(self):
        if self.type == "container.docker":
            # docker container for example
            return False
        try:
            self.getaddr()
            if not hasattr(self, 'addr'):
                Container.getaddr(self)
            if not hasattr(self, 'addr'):
                raise ex.excError()
        except:
            self.log.info("could not resolve %s to an ip address" % self.vm_hostname)
            return True

        u = __import__("rcUtilities"+rcEnv.sysname)
        ping = u.check_ping
        self.log.info("test %s ip %s availability" % (self.name, self.addr))
        if ping(self.addr):
            self.log.info("address %s is alive" % self.addr)
            return True

        return False

    def abort_start(self):
        if self.is_up():
            return False

        if self.abort_start_ping():
            return True

        nodename = self.where_up()
        if nodename is not None and nodename != rcEnv.nodename:
            return True

        return False

    def start(self):
        self.promote_rw()
        self.getaddr()
        where = self.where_up()
        if where is not None:
            self.log.info("container %s already started on %s" % (self.label, where))
            return
        if rcEnv.nodename in self.svc.drpnodes:
            self.install_drp_flag()
        self.create_pg()
        self.container_start()
        self.can_rollback = True
        self.wait_for_startup()
        self.booted = True

    def stop(self):
        self.getaddr(cache_fallback=True)
        if self.is_down():
            self.log.info("container %s already stopped" % self.label)
            return
        self.container_stop()
        try:
            self.wait_for_shutdown()
        except ex.excError:
            self.container_forcestop()
            self.wait_for_shutdown()
        if hasattr(self, "post_container_stop"):
            self.post_container_stop()

    def check_capabilities(self):
        #print("TODO: check_capabilities(self)")
        pass

    def container_start(self):
        print("TODO: container_start(self)")

    def container_stop(self):
        print("TODO: container_stop(self)")

    def container_forcestop(self):
        print("TODO: container_forcestop(self)")

    def check_manual_boot(self):
        print("TODO: check_manual_boot(self)")
        return False

    def is_up(self):
        return False

    def is_down(self):
        return not self.is_up()

    def _status(self, verbose=False):
        if self.pg_frozen():
            return rcStatus.NA
        if not self.check_manual_boot():
            self.status_log("container auto boot is on")
        try:
            self.getaddr()
        except Exception as e:
            self.status_log(str(e))
            return rcStatus.WARN
        if not self.check_capabilities():
            self.status_log("insufficient node capabilities")
            return rcStatus.WARN
        if self.is_up():
            return rcStatus.UP
        if self.is_down():
            return rcStatus.DOWN
        else:
            self.status_log("container status is neither up nor down")
            return rcStatus.WARN

    def get_container_info(self):
        print("TODO: get_container_info(self)")
        return {'vcpus': '0', 'vmem': '0'}

    def post_action(self, action):
        if action not in ("stop", "start"):
            return
        self.svc.refresh_ip_status()

    def dns_search(self):
        if self.svc.scaler_slave:
            _svcname = self.svc.svcname[self.svc.svcname.index(".")+1:]
        else:
            _svcname = self.svc.svcname
        elems = (
            "%s.%s.svc.%s" % (_svcname, self.svc.app, self.svc.cluster_name),
            "%s.svc.%s" % (self.svc.app, self.svc.cluster_name),
            "svc.%s" % self.svc.cluster_name,
        )
        return elems

0707010005c99e000081b40000000000000000000000015ab21754000025d2000000660000000000000000000000000000003100000000root/usr/share/opensvc/lib/resContainerAmazon.py  import rcStatus
import resources as Res
import time
import os
import rcExceptions as ex
from rcGlobalEnv import rcEnv
from rcUtilities import justcall
from rcUtilitiesLinux import check_ping
import resContainer

class CloudVm(resContainer.Container):
    startup_timeout = 240
    shutdown_timeout = 120
    save_timeout = 240

    def __init__(self,
                 rid,
                 name,
                 guestos=None,
                 cloud_id=None,
                 image_id=None,
                 size="t2.micro",
                 key_name=None,
                 subnet=None,
                 osvc_root_path=None,
                 **kwargs):
        resContainer.Container.__init__(self,
                                        rid=rid,
                                        name=name,
                                        type="container.amazon",
                                        guestos=guestos,
                                        osvc_root_path=osvc_root_path,
                                        **kwargs)
        self.cloud_id = cloud_id
        self.save_name = name + '.save'
        self.size_id = size
        self.image_id = image_id
        self.key_name = key_name
        self.subnet_name = subnet
        self.addr = None

    def keyfile(self):
        kf = [os.path.join(rcEnv.paths.pathetc, self.key_name+'.pem'),
              os.path.join(rcEnv.paths.pathetc, self.key_name+'.pub'),
              os.path.join(rcEnv.paths.pathvar, self.key_name+'.pem'),
              os.path.join(rcEnv.paths.pathvar, self.key_name+'.pub')]
        for k in kf:
            if os.path.exists(k):
                return k
        raise ex.excError("key file for key name '%s' not found"%self.key_name)

    def rcp_from(self, src, dst):
        if self.guestos == "Windows":
            """ Windows has no sshd.
            """
            raise ex.excNotSupported("remote copy not supported on Windows")

        self.getaddr()
        if self.addr is None:
            raise ex.excError('no usable ip to send files to')

        timeout = 5
        cmd = [ 'scp', '-o', 'StrictHostKeyChecking=no',
                       '-o', 'ConnectTimeout='+str(timeout),
                       '-i', self.keyfile(),
                        self.addr+':'+src, dst]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError("'%s' execution error:\n%s"%(' '.join(cmd), err))
        return out, err, ret

    def rcp(self, src, dst):
        if self.guestos == "Windows":
            """ Windows has no sshd.
            """
            raise ex.excNotSupported("remote copy not supported on Windows")

        self.getaddr()
        if self.addr is None:
            raise ex.excError('no usable ip to send files to')

        timeout = 5
        cmd = [ 'scp', '-o', 'StrictHostKeyChecking=no',
                       '-o', 'ConnectTimeout='+str(timeout),
                       '-i', self.keyfile(),
                        src, self.addr+':'+dst]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError("'%s' execution error:\n%s"%(' '.join(cmd), err))
        return out, err, ret

    def rcmd(self, cmd):
        if self.guestos == "Windows":
            """ Windows has no sshd.
            """
            raise ex.excNotSupported("remote commands not supported on Windows")

        self.getaddr()
        if self.addr is None:
            raise ex.excError('no usable ip to send command to')

        if type(cmd) == str:
            cmd = cmd.split(" ")

        timeout = 5
        cmd = [ 'ssh', '-o', 'StrictHostKeyChecking=no',
                       '-o', 'ForwardX11=no',
                       '-o', 'BatchMode=yes',
                       '-n',
                       '-o', 'ConnectTimeout='+str(timeout),
                       '-i', self.keyfile(),
                        self.addr] + cmd
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError("'%s' execution error:\n%s"%(' '.join(cmd), err))
        return out, err, ret

    def get_subnet(self):
        c = self.get_cloud()
        for subnet in c.driver.ex_list_subnets():
            if subnet.name == self.subnet_name:
                return subnet
        raise ex.excError("%s subnet not found"%self.subnet_name)

    def get_size(self):
        c = self.get_cloud()
        for size in c.driver.list_sizes():
            if size.id == self.size_id:
                return size
        raise ex.excError("%s size not found"%self.size_name)

    def get_cloud(self):
        if hasattr(self, 'cloud'):
            return self.cloud
        c = self.svc.node.cloud_get(self.cloud_id)
        self.cloud = c
        return self.cloud

    def get_node(self):
        c = self.get_cloud()
        l = c.list_nodes()
        for n in l:
            if n.name == self.name:
                return n
        return

    def get_image(self, image_id):
        c = self.get_cloud()
        l = c.driver.list_images(ex_image_ids=[image_id])
        d = {}
        for image in l:
            if image.id == image_id:
                # exact match
                return image
        raise ex.excError("image %s not found" % image_id)

    def has_image(self, image_id):
        c = self.get_cloud()
        l = c.driver.list_images([image_id])
        for image in l:
            if image.id == image_id:
                return True
        return False

    def __str__(self):
        return "%s name=%s" % (Res.Resource.__str__(self), self.name)

    def getaddr(self):
        if self.addr is not None:
            return
        n = self.get_node()
        if n is None:
            raise ex.excError("could not get node details")
        ips = set(n.public_ips+n.private_ips)
        if len(ips) == 0:
            return 0

        # find first pinging ip
        for ip in ips:
            if check_ping(ip, timeout=1, count=1):
                self.addr = ip
                break

        return 0

    def files_to_sync(self):
        return []

    def check_capabilities(self):
        return True

    def ping(self):
        if self.addr is None:
            return 0
        return check_ping(self.addr, timeout=1, count=1)

    def start(self):
        if self.is_up():
            self.log.info("container %s already started" % self.name)
            return
        if rcEnv.nodename in self.svc.drpnodes:
            self.install_drp_flag()
        self.container_start()
        self.can_rollback = True
        self.wait_for_startup()

    def container_start(self):
        """
	    RUNNING = 0
	    REBOOTING = 1
	    TERMINATED = 2
	    PENDING = 3
	    UNKNOWN = 4
	    STOPPED = 5
	    SUSPENDED = 6
	    ERROR = 7
	    PAUSED = 8
        """
        from libcloud.compute.types import NodeState
        n = self.get_node()
        if n is None:
            self.provision()
            return
        elif n.state == NodeState().RUNNING:
            self.log.info("already running")
            return
        elif n.state == NodeState().PENDING:
            self.log.info("already pending. wait for running state.")
            self.wait_for_fn(self.is_up, self.startup_timeout, 5)
            return
        elif n.state == NodeState().REBOOTING:
            self.log.info("currently rebooting. wait for running state.")
            self.wait_for_fn(self.is_up, self.startup_timeout, 5)
            return
        elif n.state == NodeState().STOPPED:
            c = self.get_cloud()
            self.log.info("starting ebs ec2 instance through aws")
            c.driver.ex_start_node(n)
            self.log.info("wait for container up status")
            self.wait_for_fn(self.is_up, self.startup_timeout, 5)
            return
        raise ex.excError("don't know what to do with node in state: %s"%NodeState().tostring(n.state))

    def container_reboot(self):
        c = self.get_cloud()
        n = self.get_node()
        try:
            c.driver.reboot_node(n)
        except Exception as e:
            raise ex.excError(str(e))

    def wait_for_startup(self):
        pass

    def stop(self):
        if self.is_down():
            self.log.info("container %s already stopped" % self.name)
            return
        try:
            self.container_stop()
            self.wait_for_shutdown()
        except ex.excError:
            self.container_forcestop()
            self.wait_for_shutdown()

    def container_stop(self):
        cmd = "shutdown -h now"
        self.log.info("remote command: %s"%cmd)
        self.rcmd(cmd)

    def container_forcestop(self):
        c = self.get_cloud()
        n = self.get_node()
        self.log.info("stopping ebs ec2 instance through aws")
        c.driver.ex_stop_node(n)

    def print_obj(self, n):
        for k in dir(n):
            if '__' in k:
                continue
            print(k, "=", getattr(n, k))

    def is_up(self):
        from libcloud.compute.types import NodeState
        n = self.get_node()
        if n is not None and n.state == NodeState().RUNNING:
            return True
        if n is None:
            self.status_log("state:unknown")
        else:
            self.status_log("state:"+NodeState().tostring(n.state))
        return False

    def get_container_info(self):
        self.info = {'vcpus': '0', 'vmem': '0'}
        c = self.get_cloud()
        n = self.get_node()
        try:
            size = c.driver.ex_get_size(n.extra['flavorId'])
            self.info['vmem'] = str(size.ram)
        except:
            pass
        return self.info

    def check_manual_boot(self):
        return True

    def install_drp_flag(self):
        pass


  0707010005c99f000081b40000000000000000000000015ab21754000055cb000000660000000000000000000000000000003100000000root/usr/share/opensvc/lib/resContainerDocker.py  """
Docker container resource driver module.
"""
import os
import shlex

import resources
import resContainer
import rcExceptions as ex
import rcStatus
from rcUtilitiesLinux import check_ping
from rcUtilities import justcall, lazy
from rcGlobalEnv import rcEnv

os.environ['LANG'] = 'C'

class Docker(resContainer.Container):
    """
    Docker container resource driver.
    """
    def __init__(self,
                 rid,
                 run_image,
                 run_command=None,
                 run_args=None,
                 docker_service=False,
                 guestos="Linux",
                 osvc_root_path=None,
                 **kwargs):
        resContainer.Container.__init__(self,
                                        rid=rid,
                                        name="",
                                        type="container.docker",
                                        guestos=guestos,
                                        osvc_root_path=osvc_root_path,
                                        **kwargs)

        self.run_image = run_image
        self.run_command = run_command
        self.run_args = run_args
        self.docker_service = docker_service
        self.startup_timeout = 2

    @lazy
    def container_name(self):
        """
        Format a docker container name
        """
        container_name = self.svc.svcname+'.'+self.rid
        return container_name.replace('#', '.')

    @lazy
    def service_name(self):
        """
        Format a docker compliant docker service name, ie without dots
        """
        return self.container_name.replace(".", "_")

    @lazy
    def container_id(self):
        try:
            return self.svc.dockerlib.get_container_id_by_name(self, refresh=True)
        except:
            return

    @lazy
    def service_id(self):
        try:
            return self.svc.dockerlib.get_service_id_by_name(self, refresh=True)
        except Exception:
            return

    @lazy
    def label(self):
        if self.docker_service:
            return "docker service " + "@".join((
                self.service_name,
                self.svc.dockerlib.image_userfriendly_name(self)
            ))
        else:
            return "docker container " + "@".join((
                self.container_name,
                self.svc.dockerlib.image_userfriendly_name(self)
            ))

    def __str__(self):
        return "%s name=%s" % (resources.Resource.__str__(self), self.name)

    def files_to_sync(self):
        """
        Files to contribute to sync#i0.
        """
        if self.docker_service:
            return self.svc.dockerlib.files_to_sync
        return []

    def operational(self):
        """
        Always return True for docker containers.
        """
        return True

    @lazy
    def vm_hostname(self):
        """
        Return an empty string, as we won't need that.
        """
        try:
            hostname = self.conf_get("hostname")
        except ex.OptNotFound:
            hostname = ""
        return hostname

    def get_rootfs(self):
        """
        Return the rootgs layer path.
        """
        import glob
        inspect = self.svc.dockerlib.docker_inspect(self.container_id)
        instance_id = str(inspect['Id'])
        pattern = str(self.svc.dockerlib.docker_data_dir)+"/*/mnt/"+instance_id
        fpaths = glob.glob(pattern)
        if len(fpaths) == 0:
            raise ex.excError("no candidates rootfs paths matching %s" % pattern)
        elif len(fpaths) != 1:
            raise ex.excError("too many candidates rootfs paths: %s" % ', '.join(fpaths))
        return fpaths[0]

    def rcp_from(self, src, dst):
        """
        Copy <src> from the container's rootfs to <dst> in the host's fs.
        """
        rootfs = self.get_rootfs()
        if len(rootfs) == 0:
            raise ex.excError()
        src = rootfs + src
        cmd = ['cp', src, dst]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError("'%s' execution error:\n%s" % (' '.join(cmd), err))
        return out, err, ret

    def rcp(self, src, dst):
        """
        Copy <src> from the host's fs to the container's rootfs.
        """
        rootfs = self.get_rootfs()
        if len(rootfs) == 0:
            raise ex.excError()
        dst = rootfs + dst
        cmd = ['cp', src, dst]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError("'%s' execution error:\n%s" % (' '.join(cmd), err))
        return out, err, ret

    def service_create(self):
        self.unset_lazy("service_id")
        if self.service_id is not None:
            return
        if self.swarm_node_role() not in ("leader", "reachable"):
            return
        cmd = self.svc.dockerlib.docker_cmd + ['service', 'create', '--name='+self.service_name]
        cmd += self._add_run_args()
        cmd += [self.run_image]
        if self.run_command is not None and self.run_command != "":
            cmd += self.run_command.split()
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError(err)
        self.unset_lazy("service_id")
        self.svc.dockerlib.get_running_service_ids(refresh=True)

    def container_rm(self):
        """
        Remove the resource docker instance.
        Only do if the dockerd is shared.
        """
        if self.docker_service or not self.svc.dockerlib.docker_daemon_private:
            return
        cmd = self.svc.dockerlib.docker_cmd + ['rm', self.container_name]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError(err)

    def service_rm(self):
        """
        Remove the resource docker service.
        """
        self.unset_lazy("service_id")
        if self.service_id is None:
            self.log.info("skip: service already removed")
            return
        if self.swarm_node_role() not in ("leader", "reachable"):
            return
        cmd = self.svc.dockerlib.docker_cmd + ['service', 'rm', self.service_id]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError(err)
        self.unset_lazy("service_id")
        self.svc.dockerlib.get_running_service_ids(refresh=True)

    def docker(self, action):
        """
        Wrap docker commands to honor <action>.
        """
        cmd = self.svc.dockerlib.docker_cmd + []
        if action == 'start':
            if self.docker_service:
                self.service_create()
                return
            else:
                if self.container_id is None:
                    self.unset_lazy("container_id")
                if self.container_id is None:
                    cmd += ['run', '-d', '--name='+self.container_name]
                    cmd += self._add_run_args()
                    cmd += [self.run_image]
                    if self.run_command is not None and self.run_command != "":
                        cmd += self.run_command.split()
                else:
                    cmd += ['start', self.container_id]
        elif action == 'stop':
            if self.docker_service:
                self.service_stop()
                return
            else:
                cmd += ['stop', self.container_id]
        elif action == 'kill':
            if self.docker_service:
                return 0
            else:
                cmd += ['kill', self.container_id]
        else:
            self.log.error("unsupported docker action: %s", action)
            return 1

        ret = self.vcall(cmd, warn_to_info=True)[0]
        if ret != 0:
            raise ex.excError

        if action == 'start':
            self.unset_lazy("container_id")
            self.svc.dockerlib.get_running_instance_ids(refresh=True)
        elif action in ("stop", "kill"):
            self.unset_lazy("container_id")
            self.svc.dockerlib.docker_stop()

    def service_stop(self):
        if not self.svc.dockerlib.docker_daemon_private and self.swarm_node_role() == "worker":
            self.log.info("skip: worker with shared docker daemon")
            return
        role = self.swarm_node_role()
        if self.partial_action():
            if role == "worker":
                raise ex.excError("actions on a subset of docker services are not possible from a docker worker")
            elif role in ("leader", "reachable"):
                self.service_rm()
        else:
            if role == "worker":
                self.svc.dockerlib.docker_swarm_leave()
            elif role in ("leader", "reachable"):
                self.swarm_node_drain()

    def swarm_node_drain(self):
        if self.swarm_node_role() not in ("leader", "reachable"):
            return
        node_data = self.svc.dockerlib.node_data()
        if node_data["Spec"]["Availability"] == "drain":
            return
        cmd = self.svc.dockerlib.docker_cmd + ['node', 'update', '--availability=drain', rcEnv.nodename]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError("failed to update node availabilty as drain: %s" % err)

    def swarm_node_active(self):
        if self.swarm_node_role() not in ("leader", "reachable"):
            return
        node_data = self.svc.dockerlib.node_data()
        if node_data["Spec"]["Availability"] == "active":
            return
        cmd = self.svc.dockerlib.docker_cmd + ['node', 'update', '--availability=active', rcEnv.nodename]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError("failed to update node availabilty as active: %s" % err)

    def _add_run_args(self):
        if self.run_args is None:
            return []
        args = shlex.split(self.run_args)

        # drop user specified --name. we set ours already
        for aname in ("-n", "--name"):
            if aname in args:
                idx = args.index(aname)
                del args[idx]
                if len(args) >= idx and not args[idx].startswith("-"):
                    del args[idx]

        for arg, pos in enumerate(args):
            if arg != '-p':
                continue
            if len(args) < pos + 2:
                # bad
                break
            volarg = args[pos+1]
            if ':' in volarg:
                # mapping ... check source dir presence
                elements = volarg.split(':')
                if len(elements) != 3:
                    raise ex.excError("mapping %s should be formatted as "
                                      "<src>:<dst>:<ro|rw>" % (volarg))
                if not os.path.exists(elements[0]):
                    raise ex.excError("source dir of mapping %s does not "
                                      "exist" % (volarg))
        if self.svc.dockerlib.docker_min_version("1.7") and \
           not self.docker_service and self.svc.dockerlib.docker_daemon_private:
            args += ["--cgroup-parent", self._parent_cgroup_name()]
        if not self.svc.dockerlib.docker_min_version("1.13") and "--rm" in args:
            del args[args.index("--rm")]

        def dns_opts():
            if not self.svc.node.dns or "--dns" in self.run_args:
                return []
            if "--net=container:" in self.run_args or "--net container:" in self.run_args:
                return []
            if "--net=host" in self.run_args or "--net host" in self.run_args:
                return []
            l = []
            for dns in self.svc.node.dns:
                l += ["--dns", dns]
            for search in self.dns_search():
                l += ["--dns-search", search]
            return l

        args += dns_opts()
        return args

    def _parent_cgroup_name(self):
        """
        Return the name of the container parent cgroup.
        Ex: /<svcname>/<rset>/<rid> with invalid character replaced by dots.
        """
        return os.path.join(
            os.sep,
            "opensvc",
            self.svc.svcname,
            self.rset.rid.replace(":", "."),
            self.rid.replace("#", ".")
        )

    def container_start(self):
        self.docker('start')

    def _start(self):
        self.svc.dockerlib.docker_start()
        if self.docker_service:
            self.svc.dockerlib.init_swarm()
            self.swarm_node_active()
            if self.swarm_node_role() not in ("leader", "reachable"):
                self.log.info("skip: this docker node is not swarm manager")
                return
        resContainer.Container.start(self)

    def provision(self):
        resContainer.Container.provision(self)
        self.svc.sub_set_action("ip", "provision", tags=set([self.rid]))

    def unprovision(self):
        self.svc.sub_set_action("ip", "unprovision", tags=set([self.rid]))
        resContainer.Container.unprovision(self)
        if not self.svc.dockerlib.docker_daemon_private:
            self.container_rm()

    def start(self):
        self._start()
        self.svc.sub_set_action("ip", "start", tags=set([self.rid]))

    def container_stop(self):
        self.docker('stop')

    def stop(self):
        self.svc.sub_set_action("ip", "stop", tags=set([self.rid]))
        self._stop()

    def partial_action(self):
        if not self.svc.command_is_scoped():
            return False
        all_rids = set([res.rid for res in self.svc.get_resources("container.docker") if res.docker_service])
        if len(all_rids - set(self.svc.action_rid)) > 0:
            return True
        return False

    def _stop(self):
        if not self.svc.dockerlib.docker_running():
            return
        resContainer.Container.stop(self)
        self.svc.dockerlib.get_running_instance_ids(refresh=True)
        self.svc.dockerlib.docker_stop()

    def _info(self):
        """
        Return keys to contribute to resinfo.
        """
        data = self.svc.dockerlib._info()
        return data

    def wanted_nodes_count(self):
        if rcEnv.nodename in self.svc.nodes:
            return len(self.svc.nodes)
        else:
            return len(self.svc.drpnodes)

    def run_args_replicas(self):
        elements = self.run_args.split()
        if "--mode" in elements:
            idx = elements.index("--mode")
            if "=" in elements[idx]:
                mode = elements[idx].split("=")[-1]
            else:
                mode = elements[idx+1]
            if mode == "global":
                return self.wanted_nodes_count()
        elif "--replicas" in elements:
            idx = elements.index("--replicas")
            if "=" in elements[idx]:
                return int(elements[idx].split("=")[-1])
            else:
                return int(elements[idx+1])
        else:
            return 1

    @lazy
    def service_ps(self):
        return self.svc.dockerlib.service_ps_data(self.service_id)

    def running_replicas(self, refresh=False):
        return len(self.service_running_instances(refresh=refresh))

    @lazy
    def ready_nodes(self):
        return [node["ID"] for node in self.svc.dockerlib.node_ls_data() if node["Status"]["State"] == "ready"]

    def service_running_instances(self, refresh=False):
        if refresh:
            self.unset_lazy("service_ps")
        instances = []
        for inst in self.service_ps:
            if inst["Status"]["State"] != "running":
                continue
            if inst["NodeID"] not in self.ready_nodes:
                continue
            instances.append(inst)
        return instances

    def service_hosted_instances(self, refresh=False):
        out = self.svc.dockerlib.get_ps(refresh=refresh)
        return [line.split()[0] for line in out.splitlines() \
                if self.service_name in line and \
                "Exited" not in line and \
                "Failed" not in line and \
                "Created" not in line]

    def swarm_node_role(self):
        return self.svc.dockerlib.swarm_node_role

    def _status_service_replicas_state(self):
        if self.swarm_node_role() != "leader":
            return
        for inst in self.service_ps:
            if inst["NodeID"] not in self.ready_nodes:
                continue
            if inst["DesiredState"] != inst["Status"]["State"]:
                self.status_log("instance %s in state %s, desired %s" % (inst["ID"], inst["Status"]["State"], inst["DesiredState"]))

    def _status_service_replicas(self):
        if self.swarm_node_role() != "leader":
            return
        wanted = self.run_args_replicas()
        if wanted is None:
            return
        current = self.running_replicas()
        if wanted != current:
            if current == 0:
                # a pure resource 'down' state, we don't want to cause a warn
                # at the service overall status
                level = "info"
            else:
                level = "warn"
            self.status_log("%d replicas wanted, %d currently running" % (wanted, current), level)

    def _status_service_image(self):
        if self.swarm_node_role() != "leader":
            return
        try:
            run_image_id = self.svc.dockerlib.get_run_image_id(self)
        except ValueError as exc:
            self.status_log(str(exc))
            return
        try:
            inspect = self.svc.dockerlib.docker_service_inspect(self.service_id)
        except Exception:
            return
        running_image_id = inspect['Spec']['TaskTemplate']['ContainerSpec']['Image']
        running_image_id = self.svc.dockerlib.repotag_to_image_id(running_image_id)
        if run_image_id and run_image_id != running_image_id:
            self.status_log("the service is configured with image '%s' "
                            "instead of '%s'"%(running_image_id, run_image_id))

    def _status_container_image(self):
        try:
            run_image_id = self.svc.dockerlib.get_run_image_id(self)
        except ValueError as exc:
            self.status_log(str(exc))
            return
        try:
            inspect = self.svc.dockerlib.docker_inspect(self.container_id)
        except Exception:
            return
        running_image_id = inspect['Image']
        if run_image_id and run_image_id != running_image_id:
            self.status_log("the current container is based on image '%s' "
                            "instead of '%s'"%(running_image_id, run_image_id))

    def _status(self, verbose=False):
        try:
            self.svc.dockerlib.docker_exe
        except ex.excInitError as exc:
            self.status_log(str(exc), "warn")
            return rcStatus.DOWN
        if not self.svc.dockerlib.docker_running():
            self.status_log("docker daemon is not running", "info")
            return rcStatus.DOWN
        if self.docker_service:
            if self.swarm_node_role() == "none":
                self.status_log("swarm node is not joined", "info")
                return rcStatus.DOWN
            self.svc.dockerlib.nodes_purge()
            self.running_replicas(refresh=True)
            self._status_service_image()
            self._status_service_replicas()
            self._status_service_replicas_state()
            sta = resContainer.Container._status(self, verbose)
            hosted = len(self.service_hosted_instances())
            if hosted > 0:
                self.status_log("%d/%d instances hosted" % (hosted, self.run_args_replicas()), "info")
                balance_min, balance_max = self.balance
                if hosted > balance_max:
                    self.status_log("%d>%d instances imbalance" % (hosted, balance_max), "warn")
                elif hosted < balance_min:
                    self.status_log("%d<%d instances imbalance" % (hosted, balance_min), "warn")
            elif sta == rcStatus.UP:
                sta = rcStatus.STDBY_UP
        else:
            sta = resContainer.Container._status(self, verbose)
            self._status_container_image()

        return sta

    @lazy
    def balance(self):
        replicas = self.run_args_replicas()
        nodes = self.wanted_nodes_count()
        balance = replicas // nodes
        if balance == 0:
            balance = 1
        if replicas % nodes == 0:
            return balance, balance
        else:
            return balance, balance+1

    def container_forcestop(self):
        self.docker('kill')

    def _ping(self):
        return check_ping(self.addr, timeout=1)

    def is_down(self):
        if self.docker_service:
            hosted = len(self.service_hosted_instances(refresh=True))
            if hosted > 0:
                return False
            return True
        else:
            return not self.is_up()

    def is_up(self):
        if self.svc.dockerlib.docker_daemon_private and \
           self.svc.dockerlib.docker_data_dir is None:
            self.status_log("DEFAULT.docker_data_dir must be defined")

        if not self.svc.dockerlib.docker_running():
            return False

        if self.docker_service:
            if self.swarm_node_role() == "leader":
                if self.service_id is None:
                    self.status_log("docker service is not created", "info")
                    return False
                if self.running_replicas(refresh=True) == 0:
                    return False
                if self.service_id in self.svc.dockerlib.get_running_service_ids(refresh=True):
                    return True
            else:
                return True
        else:
            if self.container_id is None:
                self.status_log("can not find container id", "info")
                return False
            if self.container_id in self.svc.dockerlib.get_running_instance_ids(refresh=True):
                return True
        return False

    def get_container_info(self):
        return {'vcpus': '0', 'vmem': '0'}

    def check_manual_boot(self):
        return True

    def check_capabilities(self):
        return True


 0707010005c9a0000081b40000000000000000000000015ab2175400000ddf000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/resContainerEsx.py import resources as Res
import os
import rcExceptions as ex
import resContainer
from rcUtilities import which

rcU = __import__("rcUtilities" + os.uname()[0])

class Esx(resContainer.Container):
    startup_timeout = 180
    shutdown_timeout = 120

    def __init__(self,
                 rid,
                 name,
                 guestos=None,
                 osvc_root_path=None,
                 **kwargs):
        resContainer.Container.__init__(self,
                                        rid=rid,
                                        name=name,
                                        guestos=guestos,
                                        type="container.esx",
                                        osvc_root_path=osvc_root_path,
                                        **kwargs)
        self.vmx = None

    def __str__(self):
        return "%s name=%s" % (Res.Resource.__str__(self), self.name)

    def vmcmd(self, l, verbose=False):
        self.find_vmx()
        if verbose:
            ret, out, err = self.vcall(['vmware-cmd', self.vmx] + l)
        else:
            ret, out, err = self.call(['vmware-cmd', self.vmx] + l)
        return ret, out, err

    def list_conffiles(self):
        return []

    def files_to_sync(self):
        return self.list_conffiles()

    def check_capabilities(self):
        return which('vmware-cmd')

    def ping(self):
        return rcU.check_ping(self.addr, timeout=1, count=1)

    def find_vmx(self):
        if self.vmx is not None:
            return self.vmx
        cmd = ['vmware-cmd', '-l']
        ret, out, err = self.call(cmd)
        if ret != 0:
            raise ex.excError
        l = out.split()
        pattern = '/'+self.name+'.vmx'
        for vmx in l:
            if pattern in vmx:
                self.vmx = vmx
        if self.vmx is None:
            self.log.error("vm %s not found on this node"%self.name)
            raise ex.excError
        return self.vmx

    def _migrate(self):
        print("TODO")
        pass

    def container_start(self):
        cmd = ['start']
        ret, buff, err = self.vmcmd(cmd, verbose=True)
        if ret != 0:
            raise ex.excError

    def container_stop(self):
        cmd = ['stop', 'soft']
        ret, buff, err = self.vmcmd(cmd, verbose=True)
        if ret != 0:
            raise ex.excError

    def container_forcestop(self):
        cmd = ['stop', 'hard']
        ret, buff, err = self.vmcmd(cmd, verbose=True)
        if ret != 0:
            raise ex.excError

    def is_up(self):
        try:
            (ret, out, err) = self.vmcmd(['getstate'])
        except:
            return False
        if ret != 0:
            return False
        l = out.split()
        if len(l) != 3:
            return False
        if l[-1] == 'on':
            return True
        return False

    def getconfig(self, key):
        cmd = ['getconfig', key]
        ret, out, err = self.vmcmd(cmd)
        if ret != 0:
            return None
        l = out.split()
        if len(l) != 3:
            return None
        return l[-1]

    def get_container_info(self):
        self.info = {'vcpus': '0', 'vmem': '0'}
        try:
            self.info['vcpus'] = self.getconfig('numvcpus')
        except:
            self.info['vcpus'] = '0'
        try:
            self.info['vmem'] = self.getconfig('memsize')
        except:
            self.info['vmem'] = '0'
        return self.info

    def check_manual_boot(self):
        """ ESX will handle the vm startup itself """
        return True
 0707010005c9a1000081b40000000000000000000000015ab2175400000fcc000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/resContainerHpVm.py    import rcStatus
import resources as Res
import time
import os
import rcExceptions as ex
from rcUtilities import qcall
from rcGlobalEnv import rcEnv
import resContainer
u = __import__('rcUtilitiesHP-UX')
import resDiskHpVm

class HpVm(resContainer.Container):
    def __init__(self,
                 rid,
                 name,
                 guestos="HP-UX",
                 osvc_root_path=None,
                 **kwargs):
        resContainer.Container.__init__(self,
                                        rid=rid,
                                        name=name,
                                        type="container.hpvm",
                                        guestos=guestos,
                                        osvc_root_path=osvc_root_path,
                                        **kwargs)
        self.vg = resVgHpVm.Vg(
          rid = 'vmdg#'+self.rid,
          name = 'vmdg_'+self.name,
          container_name = self.name
        )

    def on_add(self):
        self.vg.svc = self.svc

    def __str__(self):
        return "%s name=%s" % (Res.Resource.__str__(self), self.name)

    def files_to_sync(self):
        import glob
        a = self.vg.files_to_sync()
        guest = os.path.join(os.sep, 'var', 'opt', 'hpvm', 'guests', self.name)
        uuid = os.path.realpath(guest)
        if os.path.exists(guest):
            a.append(guest)
        if os.path.exists(uuid):
            a.append(uuid)
        if len(files) > 0:
            a += files
        return a

    def ping(self):
        return u.check_ping(self.addr, timeout=1, count=1)

    def container_start(self):
        cmd = ['/opt/hpvm/bin/hpvmstart', '-P', self.name]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def container_stop(self):
        cmd = ['/opt/hpvm/bin/hpvmstop', '-g', '-F', '-P', self.name]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def container_forcestop(self):
        cmd = ['/opt/hpvm/bin/hpvmstop', '-F', '-P', self.name]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def check_manual_boot(self):
        cmd = ['/opt/hpvm/bin/hpvmstatus', '-M', '-P', self.name]
        (ret, out, err) = self.call(cmd, cache=True)
        if ret != 0:
            return False
        if out.split(":")[11] == "Manual":
            return True
        self.log.info("Auto boot should be turned off")
        return False

    def get_container_info(self):
        cmd = ['/opt/hpvm/bin/hpvmstatus', '-M', '-P', self.name]
        (ret, out, err) = self.call(cmd, cache=True)
        self.info = {'vcpus': '0', 'vmem': '0'}
        if ret != 0:
            return self.info
        self.info['vcpus'] = out.split(':')[19].split(';')[0]
        self.info['vmem'] = out.split(':')[20].split(';')[0]
        if 'GB' in self.info['vmem']:
            self.info['vmem'] = str(1024*1024*int(self.info['vmem'].replace('GB','')))
        return self.info

    def is_up_on(self, nodename):
        return self.is_up(nodename)

    def is_up(self, nodename=None):
        cmd = ['/opt/hpvm/bin/hpvmstatus', '-M', '-P', self.name]
        if nodename is not None:
            cmd = rcEnv.rsh.split() + [nodename] + cmd
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            return False
        if out.split(":")[10] == "On":
            return True
        return False

    def check_capabilities(self):
        if os.path.exists('/opt/hpvm/bin/hpvmstatus'):
            return True
        return False

    def _migrate(self):
        cmd = ['hpvmmigrate', '-o', '-P', self.name, '-h', self.svc.options.destination_node]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def sub_disks(self):
        return self.vg.sub_disks()

    def sub_devs(self):
        return self.vg.sub_devs()

    def presync(self):
        return self.vg.presync()

    def postsync(self):
        return self.vg.postsync()
0707010005c9a2000081b40000000000000000000000015ab2175400000d21000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/resContainerJail.py    import os
from datetime import datetime

from rcGlobalEnv import rcEnv
import rcStatus
import resources as Res
from rcUtilitiesFreeBSD import check_ping
from rcUtilities import qcall
import resContainer
import rcExceptions as ex

class Jail(resContainer.Container):
    """ jail -c name=jail1
                path=/usr/local/opt/jail1.opensvc.com
                host.hostname=jail1.opensvc.com
                ip4.addr=192.168.0.208
                command=/bin/sh /etc/rc
    """
    def files_to_sync(self):
        return []

    def operational(self):
        return True

    def install_drp_flag(self):
        rootfs = self.jailroot
        flag = os.path.join(rootfs, ".drp_flag")
        self.log.info("install drp flag in container : %s"%flag)
        with open(flag, 'w') as f:
            f.write(' ')
            f.close()

    def container_start(self):
        cmd = ['jail', '-c', 'name='+self.basename, 'path='+self.jailroot,
               'host.hostname='+self.name]
        if len(self.ips) > 0:
            cmd += ['ip4.addr='+','.join(self.ips)]
        if len(self.ip6s) > 0:
            cmd += ['ip6.addr='+','.join(self.ip6s)]
        cmd += ['command=/bin/sh', '/etc/rc']
        self.log.info(' '.join(cmd))
        ret = qcall(cmd)
        if ret != 0:
            raise ex.excError

    def container_stop(self):
        cmd = ['jail', '-r', self.basename]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def container_forcestop(self):
        """ no harder way to stop a lxc container, raise to signal our
            helplessness
        """
        self.log.error("no forced stop method")
        raise ex.excError

    def ping(self):
        return check_ping(self.addr, timeout=1)

    def is_up_on(self, nodename):
        return self.is_up(nodename)

    def is_up(self, nodename=None):
        cmd = ['jls']
        if nodename is not None:
            cmd = rcEnv.rsh.split() + [nodename] + cmd
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            raise ex.excError
        for line in out.split('\n'):
            l = line.split()
            if len(l) < 4:
                continue
            if l[2] == self.name:
                return True
        return False

    def get_container_info(self):
        print("TODO: get_container_info()")
        return {'vcpus': '0', 'vmem': '0'}

    def _status(self, verbose=False):
        if self.is_up():
            return rcStatus.UP
        else:
            return rcStatus.DOWN

    def __init__(self,
                 rid,
                 name,
                 guestos="FreeBSD",
                 jailroot="/tmp",
                 ips=[],
                 ip6s=[],
                 osvc_root_path=None,
                 **kwargs):
        resContainer.Container.__init__(self,
                                        rid=rid,
                                        name=name,
                                        guestos=guestos,
                                        type="container.jail",
                                        osvc_root_path=osvc_root_path,
                                        **kwargs)
        self.jailroot = jailroot
        self.ips = ips
        self.ip6s = ip6s

    def __str__(self):
        return "%s name=%s" % (Res.Resource.__str__(self), self.name)

   0707010005c9a3000081b40000000000000000000000015ab2175400001612000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/resContainerKvm.py import rcStatus
import resources as Res
import time
import os
import rcExceptions as ex
from rcGlobalEnv import rcEnv
from rcUtilities import qcall
from rcUtilitiesLinux import check_ping
import resContainer

class Kvm(resContainer.Container):
    startup_timeout = 180
    shutdown_timeout = 120

    def __init__(self,
                 rid,
                 name,
                 guestos=None,
                 osvc_root_path=None,
                 **kwargs):
        resContainer.Container.__init__(self,
                                        rid=rid,
                                        name=name,
                                        type="container.kvm",
                                        guestos=guestos,
                                        osvc_root_path=osvc_root_path,
                                        **kwargs)
        self.cf = os.path.join(os.sep, 'etc', 'libvirt', 'qemu', name+'.xml')

    def __str__(self):
        return "%s name=%s" % (Res.Resource.__str__(self), self.name)

    def list_kvmconffiles(self):
        if os.path.exists(self.cf):
            return [self.cf]
        return []

    def files_to_sync(self):
        return self.list_kvmconffiles()

    def check_capabilities(self):
        cmd = ['virsh', 'capabilities']
        (ret, out, err) = self.call(cmd, errlog=False)
        if ret != 0:
            self.status_log("can not fetch capabilities")
            return False
        if 'hvm' not in out:
            self.status_log("hvm not supported by host")
            return False
        return True

    def ping(self):
        return check_ping(self.addr, timeout=1, count=1)

    def container_start(self):
        if not os.path.exists(self.cf):
            self.log.error("%s not found"%self.cf)
            raise ex.excError
        cmd = ['virsh', 'define', self.cf]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError
        cmd = ['virsh', 'start', self.name]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def container_stop(self):
        cmd = ['virsh', 'shutdown', self.name]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def container_forcestop(self):
        cmd = ['virsh', 'destroy', self.name]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def is_up_on(self, nodename):
        return self.is_up(nodename)

    def is_up(self, nodename=None):
        cmd = ['virsh', 'dominfo', self.name]
        if nodename is not None:
            cmd = rcEnv.rsh.split() + [nodename] + cmd
        (ret, out, err) = self.call(cmd, errlog=False)
        if ret != 0:
            return False
        if "running" in out.split():
            return True
        return False

    def get_container_info(self):
        cmd = ['virsh', 'dominfo', self.name]
        (ret, out, err) = self.call(cmd, errlog=False, cache=True)
        self.info = {'vcpus': '0', 'vmem': '0'}
        if ret != 0:
            return self.info
        for line in out.split('\n'):
            if "CPU(s):" in line: self.info['vcpus'] = line.split(':')[1].strip()
            if "Used memory:" in line: self.info['vmem'] = line.split(':')[1].strip()
        return self.info

    def check_manual_boot(self):
        cf = os.path.join(os.sep, 'etc', 'libvirt', 'qemu', 'autostart', self.name+'.xml')
        if os.path.exists(cf):
            return False
        return True

    def install_drp_flag(self):
        flag_disk_path = os.path.join(rcEnv.paths.pathvar, 'drp_flag.vdisk')

        from xml.etree.ElementTree import ElementTree, SubElement
        tree = ElementTree()
        tree.parse(self.cf)

        """ create the vdisk if it does not exist yet
        """
        if not os.path.exists(flag_disk_path):
            with open(flag_disk_path, 'w') as f:
                f.write('')
                f.close()

        """ check if drp flag is already set up
        """
        for disk in tree.getiterator("disk"):
            e = disk.find('source')
            if e is None:
                continue
            (dev, path) = e.items()[0]
            if path == flag_disk_path:
                self.log.info("flag virtual disk already exists")
                return

        """ add vdisk to the vm xml config
        """
        self.log.info("install drp flag virtual disk")
        devices = tree.find("devices")
        e = SubElement(devices, "disk", {'device': 'disk', 'type': 'file'})
        SubElement(e, "driver", {'name': 'qemu'})
        SubElement(e, "source", {'file': flag_disk_path})
        SubElement(e, "target", {'bus': 'virtio', 'dev': 'vdosvc'})
        tree.write(self.cf)

    def sub_devs(self):
        devmapping = self.devmap()
        devs = set(map(lambda x: x[0], devmapping))
        return devs

    def devmap(self):
        if hasattr(self, "devmapping"):
            return self.devmapping

        self.devmapping = []

        from xml.etree.ElementTree import ElementTree, SubElement
        tree = ElementTree()
        tree.parse(self.cf)
        for dev in tree.getiterator('disk'):
            s = dev.find('source')
            if s is None:
                 continue
            if 'dev' not in s.attrib:
                 continue
            src = s.attrib['dev']
            s = dev.find('target')
            if s is None:
                 continue
            if 'dev' not in s.attrib:
                 continue
            dst = s.attrib['dev']
            self.devmapping.append((src, dst))
        return self.devmapping


  0707010005c9a4000081b40000000000000000000000015ab217540000101c000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/resContainerLdom.py    import resources as Res
import rcExceptions as ex
from rcUtilities import qcall
from rcUtilitiesSunOS import check_ping
import resContainer
from rcGlobalEnv import rcEnv
import os

class Ldom(resContainer.Container):
    def __init__(self,
                 rid,
                 name,
                 guestos="SunOS",
                 osvc_root_path=None,
                 **kwargs):
        resContainer.Container.__init__(self,
                                        rid=rid,
                                        name=name,
                                        type="container.ldom",
                                        guestos=guestos,
                                        osvc_root_path=osvc_root_path,
                                        **kwargs)
        self.shutdown_timeout = 240
        self.sshbin = '/usr/local/bin/ssh'


    def __str__(self):
        return "%s name=%s" % (Res.Resource.__str__(self), self.name)

    def check_capabilities(self):
        cmd = ['/usr/sbin/ldm', 'list' ]
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            return False
        return True

    def state(self):
        """ ldm state : None/inactive/bound/active
            ldm list -p domainname outputs:
                VERSION
                DOMAIN|[varname=varvalue]*
        """
        cmd = ['/usr/sbin/ldm', 'list', '-p', self.name]
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            return None
        for word in out.split("|"):
            a=word.split('=')
            if len(a) == 2:
                if a[0] == 'state':
                    return a[1]
        return None

    def ping(self):
        return check_ping(self.addr)

    def container_action(self,action):
        cmd = ['/usr/sbin/ldm', action, self.name]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError
        return None

    def container_start(self):
        """ ldm bind domain
            ldm start domain
        """
        state = self.state()
        if state == 'None':
            raise ex.excError
        if state == 'inactive':
            self.container_action('bind')
            self.container_action('start')
        if state == 'bound' :
            self.container_action('start')

    def container_forcestop(self):
        """ ldm unbind domain
            ldm stop domain
        """
        if self.state == 'active':
            try:
                self.container_action('stop')
            except ex.excError:
                pass
        self.container_action('unbind')

    def container_stop(self):
        """ launch init 5 into container
            wait_for_shutdown
            ldm stop domain
            ldm unbind domain
        """
        state = self.state()
        if state == 'None':
            raise ex.excError
        if state == 'inactive':
            return None
        if state == 'bound' :
            self.container_action('unbind')
        if state == 'active' :
            cmd = rcEnv.rsh.split() + [ self.name, '/usr/sbin/init', '5' ]
            (ret, buff, err) = self.vcall(cmd)
            if ret == 0:
                try:
                    self.log.info("wait for container shutdown")
                    self.wait_for_fn(self.is_shutdown, self.shutdown_timeout, 2)
                except ex.excError:
                    pass
            self.container_forcestop()

    def check_manual_boot(self):
        cmd = ['/usr/sbin/ldm', 'list-variable', 'auto-boot?', self.name]
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            return False
        if out != 'auto-boot?=False' :
            return True
        self.log.info("Auto boot should be turned off")
        return False

    def is_shutdown(self):
        state = self.state()
        if state == 'inactive' or state == 'bound':
            return True
        return False

    def is_down(self):
        if self.state() == 'inactive':
            return True
        return False

    def is_up(self):
        if self.state() == 'active':
            return True
        return False

0707010005c9a5000081b40000000000000000000000015ab21754000039d4000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/resContainerLxc.py import os
from datetime import datetime
from subprocess import *

import sys
import rcStatus
import resources as Res
from rcUtilitiesLinux import check_ping
from rcUtilities import which, justcall, lazy
from rcGlobalEnv import rcEnv
import resContainer
import rcExceptions as ex

class Lxc(resContainer.Container):
    """
     container status transition diagram :
       ---------
      | STOPPED |<---------------
       ---------                 |
           |                     |
         start                   |
           |                     |
           V                     |
       ----------                |
      | STARTING |--error-       |
       ----------         |      |
           |              |      |
           V              V      |
       ---------    ----------   |
      | RUNNING |  | ABORTING |  |
       ---------    ----------   |
           |              |      |
      no process          |      |
           |              |      |
           V              |      |
       ----------         |      |
      | STOPPING |<-------       |
       ----------                |
           |                     |
            ---------------------
    """

    def files_to_sync(self):
        # the config file might be in a umounted fs resource
        # in which case, no need to ask for its sync as the sync won't happen
        l = []

        # replicate the config file in the system standard path
        cf = self.get_cf_path()
        if cf:
            l.append(cf)

        return l

    def rcp_from(self, src, dst):
        rootfs = self.get_rootfs()
        if len(rootfs) == 0:
            raise ex.excError()
        src = rootfs + src
        cmd = ['cp', src, dst]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError("'%s' execution error:\n%s"%(' '.join(cmd), err))
        return out, err, ret

    def rcp(self, src, dst):
        rootfs = self.get_rootfs()
        if len(rootfs) == 0:
            raise ex.excError()
        dst = rootfs + dst
        cmd = ['cp', src, dst]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError("'%s' execution error:\n%s"%(' '.join(cmd), err))
        return out, err, ret

    def lxc(self, action):
        self.find_cf()
        outf = '/var/tmp/svc_'+self.name+'_lxc_'+action+'.log'
        if action == 'start':
            cmd = ['lxc-start', '-d', '-n', self.name, '-o', outf]
            if self.cf:
                cmd += ['-f', self.cf]
        elif action == 'stop':
            cmd = ['lxc-stop', '-n', self.name, '-o', outf]
        else:
            self.log.error("unsupported lxc action: %s" % action)
            return 1

        def prex():
            os.umask(0o022)

        t = datetime.now()
        ret, out, err = self.vcall(cmd, preexec_fn=prex)
        len = datetime.now() - t
        self.log.info('%s done in %s - ret %i - logs in %s' % (action, len, ret, outf))
        if ret != 0:
            raise ex.excError

    def get_cf_value(self, param):
        self.find_cf()
        value = None
        if not os.path.exists(self.cf):
            return None
        with open(self.cf, 'r') as f:
            for line in f.readlines():
                if param not in line:
                    continue
                if line.strip()[0] == '#':
                    continue
                l = line.replace('\n', '').split('=')
                if len(l) < 2:
                    continue
                if l[0].strip() != param:
                    continue
                value = ' '.join(l[1:]).strip()
                break
        return value

    def get_rootfs(self):
        rootfs = self.get_cf_value("lxc.rootfs")
        if rootfs is None:
            self.log.error("could not determine lxc container rootfs")
            raise ex.excError
        return rootfs

    def install_drp_flag(self):
        rootfs = self.get_rootfs()
        flag = os.path.join(rootfs, ".drp_flag")
        self.log.info("install drp flag in container : %s"%flag)
        with open(flag, 'w') as f:
            f.write(' ')
            f.close()

    def set_cpuset_clone_children(self):
        ppath = "/sys/fs/cgroup/cpuset"
        if not os.path.exists(ppath):
            self.log.debug("set_clone_children: %s does not exist" % ppath)
            return
        path = "/sys/fs/cgroup/cpuset/lxc"
        val = "1"
        try:
            os.makedirs(path)
            self.log.info("mkdir %s" % path)
        except (OSError, IOError):
            # errno 17: file exists
            pass
        for parm in ("cpuset.mems", "cpuset.cpus"):
            current_val = self.get_sysfs(path, parm)
            if current_val is None:
                continue
            if current_val == "":
                parent_val = self.get_sysfs(ppath, parm)
                self.set_sysfs(path, parm, parent_val)
        parm = "cgroup.clone_children"
        current_val = self.get_sysfs(path, parm)
        if current_val is None:
            return
        if current_val == "1":
            self.log.debug("set_cpuset_clone_children: %s/%s already set to 1" % (path, parm))
            return
        self.set_sysfs(path, parm, "1")

    def get_sysfs(self, path, parm):
        fpath = os.sep.join([path, parm])
        if not os.path.exists(fpath):
            self.log.debug("get_sysfs: %s does not exist" % path)
            return
        with open(fpath, "r") as f:
            current_val = f.read().rstrip("\n")
        self.log.debug("get_sysfs: %s contains %s" % (fpath, repr(current_val)))
        return current_val

    def set_sysfs(self, path, parm, val):
        fpath = os.sep.join([path, parm])
        self.log.info("echo %s >%s" % (val, fpath))
        with open(fpath, "w") as f:
            f.write(val)

    def cleanup_cgroup(self, t="*"):
        import glob
        for p in glob.glob("/sys/fs/cgroup/%s/lxc/%s-[0-9]" % (t, self.name)) + \
                 glob.glob("/sys/fs/cgroup/%s/lxc/%s" % (t, self.name)):
            try:
                os.rmdir(p)
                self.log.info("removed leftover cgroup %s" % p)
            except Exception as e:
                self.log.debug("failed to remove leftover cgroup %s: %s" % (p, str(e)))

    def container_start(self):
        if not self.svc.create_pg:
            self.cleanup_cgroup()
        self.set_cpuset_clone_children()
        self.install_cf()
        self.lxc('start')

    def container_stop(self):
        self.links = self.get_links()
        self.install_cf()
        self.lxc('stop')

    def post_container_stop(self):
        self.cleanup_links(self.links)
        self.cleanup_cgroup()

    def container_forcestop(self):
        """ no harder way to stop a lxc container, raise to signal our
            helplessness
        """
        raise ex.excError

    def get_links(self):
        links = []
        cmd = ['lxc-info', '--name', self.name]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return []
        for line in out.splitlines():
            if line.startswith("Link:"):
                links.append(line.split()[-1].strip())
        return links

    def cleanup_link(self, link):
        cmd = ["ip", "link", "del", "dev", link]
        out, err, ret = justcall(cmd)
        if ret == 0:
            self.log.info(" ".join(cmd))
        else:
            self.log.debug(" ".join(cmd)+out+err)

    def cleanup_links(self, links):
        for link in links:
            self.cleanup_link(link)

    def _ping(self):
        return check_ping(self.addr, timeout=1)

    def is_up_on(self, nodename):
        return self.is_up(nodename)

    def is_up(self, nodename=None):
        if which("lxc-ps"):
            return self.is_up_ps(nodename=nodename)
        else:
            return self.is_up_info(nodename=nodename)

    def is_up_info(self, nodename=None):
        cmd = ['lxc-info', '--name', self.name]
        if nodename is not None:
            cmd = rcEnv.rsh.split() + [nodename] + cmd
        out, err, ret = justcall(cmd)
        if ret != 0:
            return False
        if 'RUNNING' in out:
            return True
        return False

    def is_up_ps(self, nodename=None):
        cmd = ['lxc-ps', '--name', self.name]
        if nodename is not None:
            cmd = rcEnv.rsh.split() + [nodename] + cmd
        out, err, ret = justcall(cmd)
        if ret != 0:
            return False
        if self.name in out:
            return True
        return False

    def get_container_info(self):
        cpu_set = self.get_cf_value("lxc.cgroup.cpuset.cpus")
        if cpu_set is None:
            vcpus = 0
        else:
            vcpus = len(cpu_set.split(','))
        return {'vcpus': str(vcpus), 'vmem': '0'}

    def check_manual_boot(self):
        return True

    def check_capabilities(self):
        if not which('lxc-info'):
            self.log.debug("lxc-info is not in PATH")
            return False
        return True

    def install_cf(self):
        if self.cf is None:
            return
        cf = self.get_cf_path()
        if cf is None:
            self.log.debug("could not determine the config file standard hosting directory")
            return
        if self.cf == cf:
            return
        dn = os.path.dirname(cf)
        if not os.path.isdir(dn):
            try:
                os.makedirs(dn)
            except Exception as e:
                raise ex.excError("failed to create directory %s: %s"%(dn, str(e)))
        self.log.info("install %s as %s" % (self.cf, cf))
        try:
            import shutil
            shutil.copy(self.cf, cf)
        except Exception as e:
            raise ex.excError(str(e))

    def get_cf_path(self):
        path = which('lxc-info')
        if path is None:
            return None
        dpath = os.path.dirname(path)
        if not dpath.endswith("bin"):
            return
        dpath = os.path.realpath(os.path.join(dpath, ".."))

        if dpath in (os.sep, "/usr") and os.path.exists("/var/lib/lxc"):
            return "/var/lib/lxc/%s/config" % self.name
        if dpath in ("/usr/local") and os.path.exists("/usr/local/var/lib/lxc"):
            return "/usr/local/var/lib/lxc/%s/config" % self.name
        if dpath in (os.sep, "/usr") and os.path.exists("/etc/lxc"):
            return "/etc/lxc/%s/config" % self.name

    def check_installed_cf(self):
        cf = self.get_cf_path()
        if cf is None:
            self.status_log("could not determine the config file standard hosting directory")
            return False
        if os.path.exists(cf):
            return True
        self.status_log("config file is not installed as %s" % cf)
        return False

    def _status(self, verbose=False):
        self.check_installed_cf()
        return resContainer.Container._status(self, verbose=verbose)

    def find_cf(self):
        if self.cf is not None:
            return

        d_lxc = os.path.join('var', 'lib', 'lxc')

        # seen on debian squeeze : prefix is /usr, but containers'
        # config files paths are /var/lib/lxc/$name/config
        # try prefix first, fallback to other know prefixes
        prefixes = [os.path.join(os.sep),
                    os.path.join(os.sep, 'usr'),
                    os.path.join(os.sep, 'usr', 'local')]
        for prefix in [self.prefix] + [p for p in prefixes if p != self.prefix]:
            cf = os.path.join(prefix, d_lxc, self.name, 'config')
            if os.path.exists(cf):
                cf_d = os.path.dirname(cf)
                if not os.path.exists(cf_d):
                    os.makedirs(cf_d)
                self.cf = cf
                return

        # on Oracle Linux, config is in /etc/lxc
        cf = os.path.join(os.sep, 'etc', 'lxc', self.name, 'config')
        if os.path.exists(cf):
            self.cf = cf
            return

        self.cf = None
        raise ex.excError("unable to find the container configuration file")

    @lazy
    def prefix(self):
        prefixes = [os.path.join(os.sep),
                    os.path.join(os.sep, 'usr'),
                    os.path.join(os.sep, 'usr', 'local')]
        for prefix in prefixes:
             if os.path.exists(os.path.join(prefix, 'bin', 'lxc-start')):
                 return prefix
        raise ex.excError("lxc install prefix not found")

    def __init__(self,
                 rid,
                 name,
                 guestos="Linux",
                 cf=None,
                 rcmd=None,
                 osvc_root_path=None,
                 **kwargs):
        resContainer.Container.__init__(self,
                                        rid=rid,
                                        name=name,
                                        type="container.lxc",
                                        guestos=guestos,
                                        osvc_root_path=osvc_root_path,
                                        **kwargs)

        if rcmd is not None:
            self.runmethod = rcmd
        elif which('lxc-attach') and os.path.exists('/proc/1/ns/pid'):
            self.runmethod = ['lxc-attach', '-n', name, '--']
        else:
            self.runmethod = rcEnv.rsh.split() + [name]

        if "lxc-attach" in ' '.join(self.runmethod):
            # override getaddr from parent class with a noop
            self.getaddr = self.dummy
        else:
            # enable ping test on start
            self.ping = self._ping

        self.cf = cf

    def dummy(self, cache_fallback=False):
        pass


    def operational(self):
        if not resContainer.Container.operational(self):
            return False

        cmd = self.runmethod + ['test', '-f', '/bin/systemctl']
        out, err, ret = justcall(cmd)
        if ret == 1:
            # not a systemd container. no more checking.
            self.log.debug("/bin/systemctl not found in container")
            return True

        # systemd on-demand loading will let us start the encap service before
        # the network is fully initialized, causing start issues with nfs mounts
        # and listening apps.
        # => wait for systemd default target to become active
        cmd = self.runmethod + ['systemctl', 'is-active', 'default.target']
        out, err, ret = justcall(cmd)
        if ret == 1:
            # if systemctl is-active fails, retry later
            self.log.debug("systemctl is-active failed")
            return False
        if out.strip() == "active":
            self.log.debug("systemctl is-active succeeded")
            return True

        # ok, wait some more
        self.log.debug("waiting for lxc to come up")
        return False

    def __str__(self):
        return "%s name=%s" % (Res.Resource.__str__(self), self.name)

0707010005c9a6000081b40000000000000000000000015ab2175400001f81000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/resContainerLxd.py import os
import json

import resources as Res
from rcUtilities import which, justcall, lazy
from rcGlobalEnv import rcEnv, Storage
import resContainer
import rcExceptions as ex

lxc = "/usr/bin/lxc"
lxd = "/usr/bin/lxd"

class Container(resContainer.Container):
    refresh_provisioned_on_unprovision = True

    def __init__(self,
                 rid,
                 name,
                 **kwargs):
        resContainer.Container.__init__(self,
                                        rid=rid,
                                        name=name,
                                        type="container.lxd",
                                        **kwargs)

        self.runmethod = ['lxc', 'exec', name, '--']
        self.getaddr = self.dummy

    def files_to_sync(self):
        return ["/var/lib/lxd/containers/"+self.name]

    def rcp_from(self, src, dst):
        cmd = [lxc, "file", "pull", self.name+src, dst]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError("'%s' execution error:\n%s"%(' '.join(cmd), err))
        return out, err, ret

    def rcp(self, src, dst):
        cmd = [lxc, "file", "push", src, self.name+dst]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError("'%s' execution error:\n%s"%(' '.join(cmd), err))
        return out, err, ret

    def lxc_info(self, nodename=None):
        if nodename:
            name = nodename + ":" + self.name
        else:
            name = self.name
        cmd = [lxc, "list", name, "--format", "json"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return
        try:
            return json.loads(out)[0]
        except (ValueError, IndexError):
            return

    def postsync(self):
        self.lxd_import()

    def lxd_import(self):
        if self.lxc_info() is not None:
            return
        cmd = [lxd, "import", self.name]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def lxc_start(self):
        self.lxd_import()
        cmd = [lxc, "start", self.name]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def lxc_stop(self):
        cmd = [lxc, "stop", self.name]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def set_cpuset_clone_children(self):
        ppath = "/sys/fs/cgroup/cpuset"
        if not os.path.exists(ppath):
            self.log.debug("set_clone_children: %s does not exist" % ppath)
            return
        path = "/sys/fs/cgroup/cpuset/lxc"
        val = "1"
        if not os.path.exists(path):
            self.log.info("mkdir %s" % path)
            os.makedirs(path)
        for parm in ("cpuset.mems", "cpuset.cpus"):
            current_val = self.get_sysfs(path, parm)
            if current_val is None:
                continue
            if current_val == "":
                parent_val = self.get_sysfs(ppath, parm)
                self.set_sysfs(path, parm, parent_val)
        parm = "cgroup.clone_children"
        current_val = self.get_sysfs(path, parm)
        if current_val is None:
            return
        if current_val == "1":
            self.log.debug("set_cpuset_clone_children: %s/%s already set to 1" % (path, parm))
            return
        self.set_sysfs(path, parm, "1")

    def get_sysfs(self, path, parm):
        fpath = os.sep.join([path, parm])
        if not os.path.exists(fpath):
            self.log.debug("get_sysfs: %s does not exist" % path)
            return
        with open(fpath, "r") as f:
            current_val = f.read().rstrip("\n")
        self.log.debug("get_sysfs: %s contains %s" % (fpath, repr(current_val)))
        return current_val

    def set_sysfs(self, path, parm, val):
        fpath = os.sep.join([path, parm])
        self.log.info("echo %s >%s" % (val, fpath))
        with open(fpath, "w") as f:
            f.write(val)

    def cleanup_cgroup(self, t="*"):
        import glob
        for p in glob.glob("/sys/fs/cgroup/%s/lxc/%s-[0-9]" % (t, self.name)) + \
                 glob.glob("/sys/fs/cgroup/%s/lxc/%s" % (t, self.name)):
            try:
                os.rmdir(p)
                self.log.info("removed leftover cgroup %s" % p)
            except Exception as e:
                self.log.debug("failed to remove leftover cgroup %s: %s" % (p, str(e)))

    def _migrate(self):
        cmd = [lxc, 'move', self.name, self.svc.options.destination_node+":"+self.name]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def container_start(self):
        if not self.svc.create_pg:
            self.cleanup_cgroup()
        self.set_cpuset_clone_children()
        self.lxc_start()

    def container_stop(self):
        self.lxc_stop()

    def post_container_stop(self):
        self.cleanup_links()
        self.cleanup_cgroup()

    def container_forcestop(self):
        """ no harder way to stop a lxc container, raise to signal our
            helplessness
        """
        raise ex.excError

    def get_links(self):
        data = self.lxc_info()
        if data is None:
            return  []
        return list(data.get("Ips", {}).keys())

    def cleanup_link(self, link):
        cmd = ["ip", "link", "del", "dev", link]
        out, err, ret = justcall(cmd)
        if ret == 0:
            self.log.info(" ".join(cmd))
        else:
            self.log.debug(" ".join(cmd)+out+err)

    def cleanup_links(self):
        for link in self.get_links():
            self.cleanup_link(link)

    def has_it(self):
        data = self.lxc_info()
        if data is None:
            return False
        return True

    def is_up_on(self, nodename):
        return self.is_up(nodename=nodename)

    def is_up(self, nodename=None):
        data = self.lxc_info(nodename=nodename)
        #print(json.dumps(data, indent=4))
        if data is None:
            return False
        return data["status"] == "Running"

    def get_container_info(self):
        return {'vcpus': '0', 'vmem': '0'}

    def check_manual_boot(self):
        return True

    def check_capabilities(self):
        if not which(lxc):
            self.log.debug("lxc is not in installed")
            return False
        return True

    def _status(self, verbose=False):
        return resContainer.Container._status(self, verbose=verbose)

    def dummy(self, cache_fallback=False):
        pass

    def operational(self):
        if not resContainer.Container.operational(self):
            return False

        cmd = self.runmethod + ['test', '-f', '/bin/systemctl']
        out, err, ret = justcall(cmd, stdin=self.svc.node.devnull)
        if ret == 1:
            # not a systemd container. no more checking.
            self.log.debug("/bin/systemctl not found in container")
            return True

        # systemd on-demand loading will let us start the encap service before
        # the network is fully initialized, causing start issues with nfs mounts
        # and listening apps.
        # => wait for systemd default target to become active
        cmd = self.runmethod + ['systemctl', 'is-active', 'default.target']
        out, err, ret = justcall(cmd, stdin=self.svc.node.devnull)
        if ret == 1:
            # if systemctl is-active fails, retry later
            self.log.debug("systemctl is-active failed")
            return False
        if out.strip() == "active":
            self.log.debug("systemctl is-active succeeded")
            return True

        # ok, wait some more
        self.log.debug("waiting for lxc to come up")
        return False

    def __str__(self):
        return "%s name=%s" % (Res.Resource.__str__(self), self.name)

    def abort_start(self):
        for nodename in self.svc.peers:
            if nodename == rcEnv.nodename:
                continue
            if self.is_up_on(nodename):
                self.log.error("already up on %s", nodename)
                return True
        return False

   0707010005c9a7000081b40000000000000000000000015ab2175400002a17000000660000000000000000000000000000003400000000root/usr/share/opensvc/lib/resContainerOpenstack.py   import rcStatus
import resources as Res
import time
import os
import rcExceptions as ex
from rcGlobalEnv import rcEnv
from rcUtilities import justcall
from rcUtilitiesLinux import check_ping
import resContainer

class CloudVm(resContainer.Container):
    startup_timeout = 240
    shutdown_timeout = 120
    save_timeout = 240

    def __init__(self,
                 rid,
                 name,
                 guestos=None,
                 cloud_id=None,
                 size="tiny",
                 key_name=None,
                 shared_ip_group=None,
                 osvc_root_path=None,
                 **kwargs):
        resContainer.Container.__init__(self,
                                        rid=rid,
                                        name=name,
                                        type="container.openstack",
                                        guestos=guestos,
                                        osvc_root_path=osvc_root_path,
                                        **kwargs)
        self.cloud_id = cloud_id
        self.save_name = name + '.save'
        self.size_name = size
        self.key_name = key_name
        self.shared_ip_group = shared_ip_group
        self.addr = None

    def keyfile(self):
        kf = [os.path.join(rcEnv.paths.pathetc, self.key_name+'.pem'),
              os.path.join(rcEnv.paths.pathetc, self.key_name+'.pub'),
              os.path.join(rcEnv.paths.pathvar, self.key_name+'.pem'),
              os.path.join(rcEnv.paths.pathvar, self.key_name+'.pub')]
        for k in kf:
            if os.path.exists(k):
                return k
        raise ex.excError("key file for key name '%s' not found"%self.key_name)

    def rcp_from(self, src, dst):
        if self.guestos == "Windows":
            """ Windows has no sshd.
            """
            raise ex.excNotSupported("remote copy not supported on Windows")

        self.getaddr()
        if self.addr is None:
            raise ex.excError('no usable public ip to send files to')

        timeout = 5
        cmd = [ 'scp', '-o', 'StrictHostKeyChecking=no',
                       '-o', 'ConnectTimeout='+str(timeout),
                       '-i', self.keyfile(),
                        self.addr+':'+src, dst]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError("'%s' execution error:\n%s"%(' '.join(cmd), err))
        return out, err, ret

    def rcp(self, src, dst):
        if self.guestos == "Windows":
            """ Windows has no sshd.
            """
            raise ex.excNotSupported("remote copy not supported on Windows")

        self.getaddr()
        if self.addr is None:
            raise ex.excError('no usable public ip to send files to')

        timeout = 5
        cmd = [ 'scp', '-o', 'StrictHostKeyChecking=no',
                       '-o', 'ConnectTimeout='+str(timeout),
                       '-i', self.keyfile(),
                        src, self.addr+':'+dst]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError("'%s' execution error:\n%s"%(' '.join(cmd), err))
        return out, err, ret

    def rcmd(self, cmd):
        if self.guestos == "Windows":
            """ Windows has no sshd.
            """
            raise ex.excNotSupported("remote commands not supported on Windows")

        self.getaddr()
        if self.addr is None:
            raise ex.excError('no usable public ip to send command to')

        if type(cmd) == str:
            cmd = cmd.split(" ")

        timeout = 5
        cmd = [ 'ssh', '-o', 'StrictHostKeyChecking=no',
                       '-o', 'ForwardX11=no',
                       '-o', 'BatchMode=yes',
                       '-n',
                       '-o', 'ConnectTimeout='+str(timeout),
                       '-i', self.keyfile(),
                        self.addr] + cmd
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError("'%s' execution error:\n%s"%(' '.join(cmd), err))
        return out, err, ret

    def get_size(self):
        c = self.get_cloud()
        for size in c.driver.list_sizes():
            if size.name == self.size_name:
                return size
        raise ex.excError("%s size not found"%self.size_name)

    def get_cloud(self):
        if hasattr(self, 'cloud'):
            return self.cloud
        c = self.svc.node.cloud_get(self.cloud_id)
        self.cloud = c
        return self.cloud

    def get_node(self):
        c = self.get_cloud()
        l = c.list_nodes()
        for n in l:
            if n.name == self.name:
                return n
        return

    def get_save_name(self):
        import datetime
        now = datetime.datetime.now()
        save_name = self.save_name + now.strftime(".%Y-%m-%d.%H:%M:%S")
        return save_name

    def purge_saves(self):
        c = self.get_cloud()
        l = c.driver.list_images()
        d = {}
        for image in l:
            if image.name.startswith(self.save_name):
                d[image.name] = image
        if len(d) == 0:
             raise ex.excError("no save image found")
        elif len(d) == 1:
             self.log.info("no previous save image to delete")
        for k in sorted(d.keys())[:-1]:
             self.log.info("delete previous save image %s"%d[k].name)
             c.driver.ex_delete_image(d[k])

    def get_last_save(self):
        return self.get_image(self.save_name)

    def get_template(self):
        template = self.svc.config.defaults()['template']
        return self.get_image(template)

    def get_image(self, name):
        c = self.get_cloud()
        l = c.driver.list_images()
        d = {}
        for image in l:
            if image.name == name:
                # exact match
                return image
            elif image.name.startswith(name):
                d[image.name] = image
        if len(d) == 0:
             raise ex.excError("image %s not found"%name)
        for k in sorted(d.keys()):
             last = d[k]
        return last

    def has_image(self, name):
        c = self.get_cloud()
        l = c.driver.list_images()
        for image in l:
            if image.name == name:
                return True
        return False

    def __str__(self):
        return "%s name=%s" % (Res.Resource.__str__(self), self.name)

    def getaddr(self):
        if self.addr is not None:
            return
        n = self.get_node()
        if n is None:
            raise ex.excError("could not get node details")
        ips = set(n.public_ips+n.private_ips)
        if len(ips) == 0:
            return 0

        # find first pinging ip
        for ip in ips:
            if check_ping(ip, timeout=1, count=1):
                self.addr = ip
                break

        return 0

    def files_to_sync(self):
        return []

    def check_capabilities(self):
        return True

    def ping(self):
        if self.addr is None:
            return 0
        return check_ping(self.addr, timeout=1, count=1)

    def start(self):
        if self.is_up():
            self.log.info("container %s already started" % self.name)
            return
        if rcEnv.nodename in self.svc.drpnodes:
            self.install_drp_flag()
        self.container_start()
        self.can_rollback = True
        self.wait_for_startup()

    def container_start(self):
        n = self.get_node()
        if n is not None:
            if n.state == 4:
                self.log.info("reboot %s"%self.name)
                self.container_reboot()
            else:
                raise ex.excError("abort reboot because vm is in state %d (!=4)"%n.state)
        else:
            self.container_restore()

    def container_reboot(self):
        c = self.get_cloud()
        n = self.get_node()
        try:
            c.driver.reboot_node(n)
        except Exception as e:
            raise ex.excError(str(e))

    def container_restore(self):
        c = self.get_cloud()
        image = self.get_last_save()
        size = self.get_size()
        self.log.info("create instance %s, size %s, image %s, key %s"%(self.name, size.name, image.name, self.key_name))
        n = c.driver.create_node(name=self.name, size=size, image=image, ex_keyname=self.key_name, ex_shared_ip_group_id=self.shared_ip_group)
        self.log.info("wait for container up status")
        self.wait_for_fn(self.is_up, self.startup_timeout, 5)
        #n = c.driver.ex_update_node(n, accessIPv4='46.231.128.84')

    def wait_for_startup(self):
        pass

    def stop(self):
        if self.is_down():
            self.log.info("container %s already stopped" % self.name)
            return
        self.container_stop()
        try:
            self.wait_for_shutdown()
        except ex.excError:
            self.container_forcestop()
            self.wait_for_shutdown()

    def container_stop(self):
        cmd = "shutdown -h now"
        self.log.info("remote command: %s"%cmd)
        self.rcmd(cmd)

    def container_forcestop(self):
        c = self.get_cloud()
        n = self.get_node()
        self.container_save()
        c.driver.destroy_node(n)
        self.purge_saves()

    def print_obj(self, n):
        for k in dir(n):
            if '__' in k:
                continue
            print(k, "=", getattr(n, k))

    def container_save(self):
        c = self.get_cloud()
        n = self.get_node()
        save_name = self.get_save_name()
        if self.has_image(save_name):
            return
        #self.print_obj(n)
        if n.state == 9999:
            self.log.info("a save is already in progress")
            return
        self.log.info("save new image %s"%save_name)
        try:
            image = c.driver.ex_save_image(n, save_name)
        except Exception as e:
            raise ex.excError(str(e))
        import time
        delay = 5
        for i in range(self.save_timeout//delay):
            img = c.driver.ex_get_image(image.id)
            if img.extra['status'] != 'SAVING':
                break
            time.sleep(delay)
        if img.extra['status'] != 'ACTIVE':
            raise ex.excError("save failed, image status %s"%img.extra['status'])

    def is_up(self):
        n = self.get_node()
        if n is not None and n.state == 0:
            return True
        return False

    def get_container_info(self):
        self.info = {'vcpus': '0', 'vmem': '0'}
        c = self.get_cloud()
        n = self.get_node()
        try:
            size = c.driver.ex_get_size(n.extra['flavorId'])
            self.info['vmem'] = str(size.ram)
        except:
            pass
        return self.info

    def check_manual_boot(self):
        return True

    def install_drp_flag(self):
        pass


 0707010005c9a8000081b40000000000000000000000015ab217540000138a000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/resContainerOvm.py import resources as Res
import os
import rcExceptions as ex
import resContainer
from rcGlobalEnv import rcEnv
from rcUtilities import fcache

rcU = __import__("rcUtilities" + os.uname()[0])

class Ovm(resContainer.Container):
    startup_timeout = 180
    shutdown_timeout = 120

    def __init__(self,
                 rid,
                 name,
                 uuid,
                 guestos=None,
                 osvc_root_path=None,
                 **kwargs):
        resContainer.Container.__init__(self,
                                        rid=rid,
                                        name=name,
                                        type="container.ovm",
                                        guestos=guestos,
                                        osvc_root_path=osvc_root_path,
                                        **kwargs)
        self.uuid = uuid
        self.xen_d = os.path.join(os.sep, 'etc', 'xen')
        self.xen_auto_d = os.path.join(self.xen_d, 'auto')

    def __str__(self):
        return "%s name=%s" % (Res.Resource.__str__(self), self.name)

    def list_conffiles(self):
        cf = os.path.join(self.xen_d, self.uuid)
        if os.path.exists(cf):
            return [cf]
        return []

    def files_to_sync(self):
        return self.list_conffiles()

    def check_capabilities(self):
        cmd = ['xm', 'info']
        (ret, out, err) = self.call(cmd, errlog=False)
        if ret != 0:
            self.status_log("can not fetch xm info")
            return False
        return True

    def ping(self):
        return rcU.check_ping(self.addr, timeout=1, count=1)

    def find_vmcf(self):
        import glob
        l = glob.glob('/OVS/Repositories/*/VirtualMachines/'+self.uuid+'/vm.cfg')+glob.glob(os.path.join(self.xen_d, self.uuid))
        if len(l) > 1:
            self.log.warning("%d configuration files found in repositories (%s)"%(len(l), str(l)))
        elif len(l) == 0:
            raise ex.excError("no configuration file found in repositories")
        return l[0]

    def _migrate(self):
        cmd = ['xm', 'migrate', '-l', self.uuid, self.svc.options.destination_node]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def container_start(self):
        cf = self.find_vmcf()
        cmd = ['xm', 'create', cf]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def container_stop(self):
        cmd = ['xm', 'shutdown', self.uuid]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def container_forcestop(self):
        cmd = ['xm', 'destroy', self.uuid]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def is_up_on(self, nodename):
        return self.is_up(nodename)

    def is_up(self, nodename=None):
        cmd = ['xm', 'list', '--state=running']
        if nodename is not None:
            cmd = rcEnv.rsh.split() + [nodename] + cmd
        (ret, out, err) = self.call(cmd, errlog=False)
        if ret != 0:
            return False
        for line in out.split('\n'):
            l = line.split()
            if len(l) < 4:
                continue
            if self.uuid == l[0]:
                return True
        return False

    def get_container_info(self):
        cmd = ['xm', 'list', self.uuid]
        (ret, out, err) = self.call(cmd, errlog=False, cache=True)
        self.info = {'vcpus': '0', 'vmem': '0'}
        if ret != 0:
            return self.info
        for line in out.split('\n'):
            l = line.split()
            if len(l) < 4:
                continue
            if self.uuid != l[0]:
                continue
            self.info['vcpus'] = l[3]
            self.info['vmem'] = l[2]
            return self.info
        self.log.error("malformed 'xm list %s' output: %s"%(self.uuid, line))
        self.info = {'vcpus': '0', 'vmem': '0'}
        return self.info

    def check_manual_boot(self):
        f = os.path.join(self.xen_auto_d, self.uuid)
        if os.path.exists(f):
            return False
        return True

    @fcache
    def devmap(self):
        devmapping = []

        cf = self.find_vmcf()
        with open(cf, 'r') as f:
            buff = f.read()

        for line in buff.split('\n'):
            if not line.startswith('disk'):
                continue
            disks = line[line.index('['):]
            if len(line) <= 2:
                break
            disks = disks[1:-1]
            disks = disks.split(', ')
            for disk in disks:
                disk = disk.strip("'")
                d = disk.split(',')
                if not d[0].startswith('phy:'):
                    continue
                l = [d[0].strip('phy:'), d[1]]
                devmapping.append(l)
            break

        return devmapping

    def sub_devs(self):
        devs = set(map(lambda x: x[0], self.devmap()))
        return devs

  0707010005c9a9000081b40000000000000000000000015ab2175400001f05000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/resContainerSrp.py import os

from rcGlobalEnv import rcEnv
import rcStatus
import resources as Res
from rcUtilities import which, qcall, justcall, lazy
import resContainer
import rcExceptions as ex

class Srp(resContainer.Container):
    def files_to_sync(self):
        return [self.export_file]

    def get_rootfs(self):
        return self.get_state()['state']

    def rcp_from(self, src, dst):
        rootfs = self.get_rootfs()
        if len(rootfs) == 0:
            raise ex.excError()
        src = rootfs + src
        cmd = ['cp', src, dst]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError("'%s' execution error:\n%s"%(' '.join(cmd), err))
        return out, err, ret

    def rcp(self, src, dst):
        rootfs = self.get_rootfs()
        if len(rootfs) == 0:
            raise ex.excError()
        dst = rootfs + dst
        cmd = ['cp', src, dst]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError("'%s' execution error:\n%s"%(' '.join(cmd), err))
        return out, err, ret

    def install_drp_flag(self):
        rootfs = self.get_rootfs()
        flag = os.path.join(rootfs, ".drp_flag")
        self.log.info("install drp flag in container : %s"%flag)
        with open(flag, 'w') as f:
            f.write(' ')
            f.close()

    def container_start(self):
        cmd = ['srp', '-start', self.name]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError()

    def container_stop(self):
        cmd = ['srp', '-stop', self.name]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError()

    def container_forcestop(self):
        raise ex.excError

    def operational(self):
        cmd = self.runmethod + ['pwd']
        ret = qcall(cmd)
        if ret == 0:
            return True
        return False

    def get_verbose_list(self):
        """
        Name: iisdevs1  Template: system Service: provision ID: 1
        ----------------------------------------------------------------------

        autostart=0
        srp_name=iisdevs1
        ...
        """
        cmd = ['srp', '-list', self.name, '-v']
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError("srp -list returned %d:\n%s"%(ret, err))

        data = {}

        words = out.split()
        for i, w in enumerate(words):
            if w == "Service:":
                service = words[i+1]
            if '=' in w:
                key = service + '.' + w[:w.index('=')]
                val = w[w.index('=')+1:]
                data[key] = val
        return data

    def get_verbose_status(self):
        """
        SRP Status:

        ----------------- Status for SRP:iisdevs1 ----------------------
            Status:MAINTENANCE

            Type:system    Subtype:private    Rootpath:/var/hpsrp/iisdevs1

            IP:10.102.184.12      Interface:lan0:1 (DOWN)   id: 1
            MEM Entitle:50.00%    MEM Max:(none)    Usage:0.00%
            CPU Entitle:9.09%    CPU Max:(none)    Usage:0.00%
        """
        cmd = ['srp', '-status', self.name, '-v']
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError("srp -status returned %d:\n%s"%(ret, err))

        data = {'ip': [], 'mem': {}, 'cpu': {}}

        words = out.split()
        for i, w in enumerate(words):
            if w.startswith('IP'):
                ip = w.replace('IP:','')
                intf = words[i+1].replace('Interface:','')
                state = words[i+2].strip('(').strip(')')
                id = words[i+4]
                data['ip'].append({
                  'ip': ip,
                  'intf': intf,
                  'state': state,
                  'id': id,
                })
            elif w == "MEM" and words[i+1].startswith("Entitle"):
                entitle = words[i+1].replace('Entitle:','')
                max = words[i+3].replace('Max:','')
                usage = words[i+4].replace('Usage:','')
                data['mem'] = {
                  'entitle': entitle,
                  'max': max,
                  'usage': usage,
                }
            elif w == "CPU" and words[i+1].startswith("Entitle"):
                entitle = words[i+1].replace('Entitle:','')
                max = words[i+3].replace('Max:','')
                usage = words[i+4].replace('Usage:','')
                data['cpu'] = {
                  'entitle': entitle,
                  'max': max,
                  'usage': usage,
                }
        return data

    def get_status(self, nodename=None):
        """
        NAME         TYPE      STATE       SUBTYPE    ROOTPATH
        iisdevs1     system    maintenance private    /var/hpsrp/iisdevs1
        """
        cmd = ['srp', '-status', self.name]
        if nodename is not None:
            cmd = rcEnv.rsh.split() + [nodename] + cmd

        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError("srp -status returned %d:\n%s"%(ret, err))
        lines = out.split('\n')
        if len(lines) < 2:
            raise ex.excError("srp -status output too short:\n%s"%out)
        l = lines[1].split()
        if l[0] != self.name:
            raise ex.excError("srp -status second line, first entry does not match container name")
        if len(l) != 5:
            raise ex.excError("unexpected number of entries in %s"%str(l))
        _type, _state, _subtype, _rootpath = l[1:]
        return {
          'type': l[1],
          'state': l[2],
          'subtype': l[3],
          'rootpath': l[4],
        }

    def is_down(self):
        d = self.get_status()
        if d['state'] == 'stopped':
            return True
        return False

    def is_up_on(self, nodename):
        return self.is_up(nodename)

    def is_up(self, nodename=None):
        d = self.get_status(nodename)
        if d['state'] == 'started':
            return True
        return False

    def get_container_info(self):
        return {'vcpus': '0', 'vmem': '0'}

    def check_manual_boot(self):
        try:
            val = self.get_verbose_list()['init.autostart']
        except ex.excError:
            return False
        if val == 'yes' or val == '1':
            return False
        return True

    def check_capabilities(self):
        if not which('srp'):
            self.log.debug("srp is not in PATH")
            return False
        return True

    def presync(self):
        self.container_export()

    def postsync(self):
        self.container_import()

    def container_import(self):
        if not os.path.exists(self.export_file):
            raise ex.excError("%s does not exist"%self.export_file)
        cmd = ['srp', '-batch', '-import', '-xfile', self.export_file, 'allow_sw_mismatch=yes', 'autostart=no']
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError()

    def container_export(self):
        cmd = ['srp', '-batch', '-export', self.name, '-xfile', self.export_file]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError()

    def __init__(self,
                 rid,
                 name,
                 guestos="HP-UX",
                 osvc_root_path=None,
                 **kwargs):
        resContainer.Container.__init__(self,
                                        rid=rid,
                                        name=name,
                                        type="container.srp",
                                        guestos=guestos,
                                        osvc_root_path=osvc_root_path,
                                        **kwargs)
        self.runmethod = ['srp_su', name, 'root', '-c']

    @lazy
    def export_file(self):
        return os.path.join(self.var_d, name + '.xml')

    def __str__(self):
        return "%s name=%s" % (Res.Resource.__str__(self), self.name)

   0707010005c9aa000081b40000000000000000000000015ab2175400000fd4000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/resContainerVbox.py    import resources as Res
import rcExceptions as ex
from rcUtilities import qcall
import resContainer
from rcGlobalEnv import rcEnv
import os

utilities = __import__('rcUtilities'+rcEnv.sysname)

class Vbox(resContainer.Container):
    def __init__(self,
                 rid,
                 name,
                 guestos=None,
                 osvc_root_path=None,
                 **kwargs):
        resContainer.Container.__init__(self,
                                        rid=rid,
                                        name=name,
                                        type="container.vbox",
                                        guestos=guestos,
                                        osvc_root_path=osvc_root_path,
                                        **kwargs)
        self.shutdown_timeout = 240
        #self.sshbin = '/usr/local/bin/ssh'
        self.vminfo = None

    def __str__(self):
        return "%s name=%s" % (Res.Resource.__str__(self), self.name)

    def get_vminfo(self):
        if self.vminfo is not None:
            return self.vminfo
        cmd = ['VBoxManage', 'showvminfo', '--machinereadable', self.name]
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            return None
        h = {}
        for line in out.split('\n'):
            l = line.split('=')
            if len(l) != 2:
                continue
            key = l[0].strip('"')
            val = l[1].strip('"')
            h[key] = val
        self.vminfo = h
        return self.vminfo

    def files_to_sync(self):
        a = []
        vminfo = self.get_vminfo()
        if vminfo is None:
            return []
        a.append(vminfo['CfgFile'])
        a.append(vminfo['SnapFldr'])
        a.append(vminfo['LogFldr'])
        return a

    def check_capabilities(self):
        cmd = ['VBoxManage', '-v']
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            return False
        return True

    def state(self, nodename):
        cmd = ['VBoxManage', 'list', 'runningvms']
        if nodename is not None:
            cmd = rcEnv.rsh.split() + [nodename] + cmd
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            return None
        for line in out.split('\n'):
            l = line.split('"')
            if len(l) < 2:
                continue
            if l[1] == self.name:
                return 'on'
        return 'off'

    def ping(self):
        return utilities.check_ping(self.addr)

    def container_action(self, action, add=[]):
        cmd = ['VBoxManage', action, self.name] + add
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def container_start(self):
        state = self.state()
        if state == 'None':
            raise ex.excError
        elif state == 'off':
            self.container_action('startvm')
        elif state == 'on':
            self.log.info("container is already up")

    def container_forcestop(self):
        self.container_action('controlvm', ['poweroff'])

    def container_stop(self):
        state = self.state()
        if state == 'None':
            raise ex.excError
        elif state == 'off':
            self.log.info("container is already down")
        if state == 'on' :
            self.container_action('controlvm', ['acpipowerbutton'])
            try:
                self.log.info("wait for container shutdown")
                self.wait_for_fn(self.is_shutdown, self.shutdown_timeout, 2)
            except ex.excError:
                self.container_forcestop()

    def check_manual_boot(self):
        return True

    def is_shutdown(self):
        state = self.state()
        if state == 'off':
            return True
        return False

    def is_down(self):
        if self.state() == 'off':
            return True
        return False

    def is_up_on(self, nodename):
        return self.is_up(nodename)

    def is_up(self, nodename=None):
        if self.state(nodename) == 'on':
            return True
        return False

0707010005c9ab000081b40000000000000000000000015ab21754000022c0000000660000000000000000000000000000003100000000root/usr/share/opensvc/lib/resContainerVcloud.py  import rcStatus
import resources as Res
import time
import os
import rcExceptions as ex
from rcGlobalEnv import rcEnv
from rcUtilities import qcall
from rcUtilitiesLinux import check_ping
import resContainer

try:
    from libcloud.utils.py3 import urlparse
    urlparse = urlparse.urlparse
    def get_url_path(url):
        return urlparse(url.strip()).path
except ImportError:
    urlparse = None
    def get_url_path(url):
        return

class CloudVm(resContainer.Container):
    startup_timeout = 240
    shutdown_timeout = 120
    save_timeout = 240

    def __init__(self,
                 rid,
                 name,
                 vapp=None,
                 guestos=None,
                 cloud_id=None,
                 size="tiny",
                 key_name=None,
                 shared_ip_group=None,
                 osvc_root_path=None,
                 **kwargs):
        resContainer.Container.__init__(self,
                                        rid=rid,
                                        name=name,
                                        type="container.vcloud",
                                        guestos=guestos,
                                        osvc_root_path=osvc_root_path,
                                        **kwargs)
        self.cloud_id = cloud_id
        self.save_name = name + '.save'
        self.size_name = size
        self.key_name = key_name
        self.vapp = vapp
        self.shared_ip_group = shared_ip_group

    def _vm_perform_power_operation(self, vapp_or_vm_id, operation):
        drv = self.get_cloud().driver
        vms = drv._get_vm_elements(vapp_or_vm_id)
        for vm in vms:
            path = get_url_path(vm.get('href'))
            if path is None:
                raise ex.excError("libcloud is not installed")
            res = drv.connection.request(
                '%s/power/action/%s' % (path, operation),
                method='POST')
            drv._wait_for_task_completion(path)
            res = drv.connection.request(path)

    def get_size(self):
        c = self.get_cloud()
        for size in c.driver.list_sizes():
            if size.name == self.size_name:
                return size
        raise ex.excError("%s size not found"%self.size_name)

    def get_cloud(self):
        if hasattr(self, 'cloud'):
            return self.cloud
        c = self.svc.node.cloud_get(self.cloud_id)
        self.cloud = c
        return self.cloud

    def get_vapp(self):
        c = self.get_cloud()
        try:
            vapp = c.driver.ex_find_node(self.vapp)
        except Exception as e:
            print(e)
            raise
        return vapp

    def get_node(self):
        c = self.get_cloud()
        try:
            vapp = c.driver.ex_find_node(self.vapp)
            vms = vapp.extra['vms']
        except Exception as e:
            print(e)
            raise
        for vm in vms:
            if vm['name'] == self.name:
                return vm
        return

    def get_save_name(self):
        import datetime
        now = datetime.datetime.now()
        save_name = self.save_name + now.strftime(".%Y-%m-%d.%H:%M:%S")
        return save_name

    def purge_saves(self):
        c = self.get_cloud()
        l = c.driver.list_images()
        d = {}
        for image in l:
            if image.name.startswith(self.save_name):
                d[image.name] = image
        if len(d) == 0:
             raise ex.excError("no save image found")
        elif len(d) == 1:
             self.log.info("no previous save image to delete")
        for k in sorted(d.keys())[:-1]:
             self.log.info("delete previous save image %s"%d[k].name)
             c.driver.ex_delete_image(d[k])

    def get_last_save(self):
        return self.get_image(self.save_name)

    def get_template(self):
        template = self.svc.config.defaults()['template']
        return self.get_image(template)

    def get_image(self, name):
        c = self.get_cloud()
        l = c.driver.list_images()
        d = {}
        for image in l:
            if image.name == name:
                # exact match
                return image
            elif image.name.startswith(name):
                d[image.name] = image
        if len(d) == 0:
             raise ex.excError("image %s not found"%name)
        for k in sorted(d.keys()):
             last = d[k]
        return last

    def has_image(self, name):
        c = self.get_cloud()
        l = c.driver.list_images()
        for image in l:
            if image.name == name:
                return True
        return False

    def __str__(self):
        return "%s name=%s" % (Res.Resource.__str__(self), self.name)

    def getaddr(self):
        if hasattr(self, 'addr'):
            return
        n = self.get_node()

        ips = set(n['public_ips']+n['private_ips'])
        if len(ips) == 0:
            return 0

        # find first pinging ip
        for ip in ips:
            if check_ping(ip, timeout=1, count=1):
                self.addr = ip
                break

    def files_to_sync(self):
        return []

    def check_capabilities(self):
        return True

    def ping(self):
        if not hasattr(self, "addr"):
            return 0
        return check_ping(self.addr, timeout=1, count=1)

    def start(self):
        if self.is_up():
            self.log.info("container %s already started" % self.name)
            return
        if rcEnv.nodename in self.svc.drpnodes:
            self.install_drp_flag()
        self.container_start()
        self.can_rollback = True
        self.wait_for_startup()

    def container_start(self):
        c = self.get_cloud()
        n = self.get_node()
        self.log.info("power on container %s"%self.name)
        self._vm_perform_power_operation(n['id'], 'powerOn')
        self.log.info("wait for container up status")
        self.wait_for_fn(self.is_up, self.startup_timeout, 5)

    def wait_for_startup(self):
        pass

    def stop(self):
        if self.is_down():
            self.log.info("container %s already stopped" % self.name)
            return
        self.container_stop()
        try:
            self.wait_for_shutdown()
        except ex.excError:
            self.container_forcestop()
            self.wait_for_shutdown()

    def container_stop(self):
        c = self.get_cloud()
        n = self.get_node()
        self.log.info("shutdown container %s"%self.name)
        self._vm_perform_power_operation(n['id'], 'shutdown')

    def print_obj(self, n):
        for k in dir(n):
            if '__' in k:
                continue
            print(k, "=", getattr(n, k))

    def container_save(self):
        c = self.get_cloud()
        n = self.get_node()
        save_name = self.get_save_name()
        if self.has_image(save_name):
            return
        #self.print_obj(n)
        if n['state'] == 9999:
            self.log.info("a save is already in progress")
            return
        self.log.info("save new image %s"%save_name)
        try:
            image = c.driver.ex_save_image(n, save_name)
        except Exception as e:
            raise ex.excError(str(e))
        import time
        delay = 5
        for i in range(self.save_timeout//delay):
            img = c.driver.ex_get_image(image.id)
            if img.extra['status'] != 'SAVING':
                break
            time.sleep(delay)
        if img.extra['status'] != 'ACTIVE':
            raise ex.excError("save failed, image status %s"%img.extra['status'])

    def container_forcestop(self):
        c = self.get_cloud()
        n = self.get_node()
        self.log.info("power off container %s"%self.name)
        self._vm_perform_power_operation(n['id'], 'powerOff')

    def is_up(self):
        n = self.get_node()
        if n is not None and n['state'] == 0:
            return True
        return False

    def get_container_info(self):
        self.info = {'vcpus': '0', 'vmem': '0'}
        c = self.get_cloud()
        n = self.get_node()
        top = c.driver._get_vm_elements(n['id'])[0]
        def recurse(x, info={}, desc=None):
            if x.tag == '{http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData}Description':
                desc = x.text
                info[desc] = {}
                return info, desc
            if desc is not None:
                info[desc][x.tag[x.tag.index('}')+1:]] = x.text
            for c in x._children:
                info, desc = recurse(c, info, desc)
            return info, desc
        info, desc = recurse(top)
        self.info['vcpus'] = info['Number of Virtual CPUs']['VirtualQuantity']
        self.info['vmem'] = info['Memory Size']['VirtualQuantity']
        #print(self.info)
        return self.info

    def check_manual_boot(self):
        return True

    def install_drp_flag(self):
        pass


0707010005c9ac000081b40000000000000000000000015ab21754000012dc000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/resContainerVz.py  import os

import rcStatus
import resources as Res
from rcUtilities import which, qcall, justcall
import resContainer
import rcExceptions as ex
from rcGlobalEnv import rcEnv

class Vz(resContainer.Container):
    def files_to_sync(self):
        return [self._cf]

    def get_cf_value(self, param):
        value = None
        try:
            cf = self.cf()
        except:
            return value
        with open(cf, 'r') as f:
            for line in f.readlines():
                if param not in line:
                    continue
                if line.strip()[0] == '#':
                    continue
                l = line.replace('\n', '').split('=')
                if len(l) < 2:
                    continue
                if l[0].strip() != param:
                    continue
                value = ' '.join(l[1:]).strip().rstrip('/')
                break
        return value

    def get_rootfs(self):
        with open(self.cf(), 'r') as f:
            for line in f:
                if 'VE_PRIVATE' in line:
                    return line.strip('\n').split('=')[1].strip('"').replace('$VEID', self.name)
        self.log.error("could not determine lxc container rootfs")
        return ex.excError

    def rcp_from(self, src, dst):
        rootfs = self.get_rootfs()
        if len(rootfs) == 0:
            raise ex.excError()
        src = rootfs + src
        cmd = ['cp', src, dst]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError("'%s' execution error:\n%s"%(' '.join(cmd), err))
        return out, err, ret

    def rcp(self, src, dst):
        rootfs = self.get_rootfs()
        if len(rootfs) == 0:
            raise ex.excError()
        dst = rootfs + dst
        cmd = ['cp', src, dst]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError("'%s' execution error:\n%s"%(' '.join(cmd), err))
        return out, err, ret

    def install_drp_flag(self):
        rootfs = self.get_rootfs()
        flag = os.path.join(rootfs, ".drp_flag")
        self.log.info("install drp flag in container : %s"%flag)
        with open(flag, 'w') as f:
            f.write(' ')
            f.close()

    def vzctl(self, action, options=[]):
        cmd = ['vzctl', action, self.name] + options
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError
        return out

    def container_start(self):
        self.vzctl('start')

    def container_stop(self):
        self.vzctl('stop')

    def container_forcestop(self):
        raise ex.excError

    def operational(self):
        cmd = self.runmethod + ['/sbin/ifconfig', '-a']
        ret = qcall(cmd)
        if ret == 0:
            return True
        return False

    def is_up_on(self, nodename):
        return self.is_up(nodename)

    def is_up(self, nodename=None):
        """ CTID 101 exist mounted running
        """
        cmd = ['vzctl', 'status', self.name]
        if nodename is not None:
            cmd = rcEnv.rsh.split() + [nodename] + cmd
        ret, out, err = self.call(cmd)
        if ret != 0:
            return False
        l = out.split()
        if len(l) != 5:
            return False
        if l[2] != 'exist' or \
           l[3] != 'mounted' or \
           l[4] != 'running':
            return False
        return True

    def get_container_info(self):
        return {'vcpus': '0', 'vmem': '0'}

    def check_manual_boot(self):
        try:
            cf = self.cf()
        except:
            return True
        with open(self.cf(), 'r') as f:
            for line in f:
                if 'ONBOOT' in line and 'yes' in line:
                    return False
        return True

    def check_capabilities(self):
        if not which('vzctl'):
            self.log.debug("vzctl is not in PATH")
            return False
        return True

    def cf(self):
        if not os.path.exists(self._cf):
            self.log.error("%s does not exist"%self._cf)
            raise ex.excError
        return self._cf

    def __init__(self,
                 rid,
                 name,
                 guestos="Linux",
                 osvc_root_path=None,
                 **kwargs):
        resContainer.Container.__init__(self,
                                        rid=rid,
                                        name=name,
                                        type="container.vz",
                                        guestos=guestos,
                                        osvc_root_path=osvc_root_path,
                                        **kwargs)
        self._cf = os.path.join(os.sep, 'etc', 'vz', 'conf', name+'.conf')
        self.runmethod = ['vzctl', 'exec', name]

    def __str__(self):
        return "%s name=%s" % (Res.Resource.__str__(self), self.name)

0707010005c9ad000081b40000000000000000000000015ab2175400000d7e000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/resContainerXen.py from rcGlobalEnv import rcEnv
import resources as Res
import os
import rcExceptions as ex
import resContainer

rcU = __import__("rcUtilities" + os.uname()[0])

class Xen(resContainer.Container):
    startup_timeout = 180
    shutdown_timeout = 120

    def __init__(self,
                 rid,
                 name,
                 guestos=None,
                 osvc_root_path=None,
                 **kwargs):
        resContainer.Container.__init__(self,
                                        rid=rid,
                                        name=name,
                                        type="container.xen",
                                        guestos=guestos,
                                        osvc_root_path=osvc_root_path,
                                        **kwargs)

    def __str__(self):
        return "%s name=%s" % (Res.Resource.__str__(self), self.name)

    def list_conffiles(self):
        cf = os.path.join(os.sep, 'opt', 'opensvc', 'var', self.name+'.xml')
        if os.path.exists(cf):
            return [cf]
        return []

    def files_to_sync(self):
        return self.list_conffiles()

    def check_capabilities(self):
        cmd = ['virsh', 'capabilities']
        (ret, out, err) = self.call(cmd, errlog=False)
        if ret != 0:
            self.status_log("can not fetch capabilities")
            return False
        return True

    def ping(self):
        return rcU.check_ping(self.addr, timeout=1, count=1)

    def container_start(self):
        cf = os.path.join(os.sep, 'opt', 'opensvc', 'var', self.name+'.xml')
        if os.path.exists(cf):
            cmd = ['virsh', 'define', cf]
            (ret, buff, err) = self.vcall(cmd)
            if ret != 0:
                raise ex.excError
        cmd = ['virsh', 'start', self.name]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def container_stop(self):
        cmd = ['virsh', 'shutdown', self.name]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def container_forcestop(self):
        cmd = ['virsh', 'destroy', self.name]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def is_up_on(self, nodename):
        return self.is_up(nodename)

    def is_up(self, nodename=None):
        cmd = ['virsh', 'dominfo', self.name]
        if nodename is not None:
            cmd = rcEnv.rsh.split() + [nodename] + cmd
        (ret, out, err) = self.call(cmd, errlog=False)
        if ret != 0:
            return False
        if "running" in out.split() or "idle" in out.split() :
            return True
        return False

    def get_container_info(self):
        cmd = ['virsh', 'dominfo', self.name]
        (ret, out, err) = self.call(cmd, errlog=False, cache=True)
        self.info = {'vcpus': '0', 'vmem': '0'}
        if ret != 0:
            return self.info
        for line in out.split('\n'):
            if "CPU(s):" in line: self.info['vcpus'] = line.split(':')[1].strip()
            if "Max memory" in line: self.info['vmem'] = line.split(':')[1].strip()
            if "Autostart:" in line: self.info['autostart'] = line.split(':')[1].strip()
        return self.info

    def check_manual_boot(self):
        self.get_container_info()
        if self.info['autostart'] == 'disable' :
                return True
        else:
                return False
  0707010005c9ae000081b40000000000000000000000015ab21754000043b6000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/resContainerZone.py    from datetime import datetime
import rcStatus
import resources as Res
import time
import os
from rcUtilities import justcall, qcall
from stat import *
import resContainer
import rcExceptions as ex
from rcZfs import zfs_setprop
from rcGlobalEnv import rcEnv

ZONECFG="/usr/sbin/zonecfg"
PGREP="/usr/bin/pgrep"
PWAIT="/usr/bin/pwait"
INIT="/sbin/init"
SVCS="/usr/bin/svcs"

MULTI_USER_SMF="svc:/milestone/multi-user:default"

class Zone(resContainer.Container):
    """
    Zone container resource driver.
    """
    def __init__(self,
                 rid,
                 name,
                 guestos="SunOS",
                 delete_on_stop=False,
                 osvc_root_path=None,
                 **kwargs):
        resContainer.Container.__init__(self,
                                        rid=rid,
                                        name=name,
                                        type="container.zone",
                                        guestos=guestos,
                                        osvc_root_path=osvc_root_path,
                                        **kwargs)
        self.label = name
        self.state = None
        self.delete_on_stop = delete_on_stop
        self.zone_refresh()
        self.runmethod = [ '/usr/sbin/zlogin', '-S', name ]
        self.zone_cf = "/etc/zones/"+self.name+".xml"
        self.delayed_noaction = True

    def zone_cfg_dir(self):
        return os.path.join(self.var_d, "zonecfg")

    def zone_cfg_path(self):
        return os.path.join(self.zone_cfg_dir(), self.name+".cfg")

    def export_zone_cfg(self):
        cfg_d = self.zone_cfg_dir()
        if not os.path.exists(cfg_d):
            os.makedirs(cfg_d)

        cfg = self.zone_cfg_path()
        cmd = [ZONECFG, "-z", self.name, "export", "-f", cfg]
        ret, out, err = self.vcall(cmd)
        if ret != 0 and not os.path.exists(cfg):
            raise ex.excError(err)

    def get_zonepath_from_zonecfg_cmd(self):
        cmd = [ZONECFG, '-z', self.name, 'info', 'zonepath']
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError("unable to determine zonepath using %s"%' '.join(cmd))
        zp = out.replace("zonepath: ", "").strip()
        return zp

    def get_zonepath_from_zonecfg_export(self):
        fpath = self.zone_cfg_path()
        if not os.path.exists(fpath):
            raise ex.excError("zone config export file %s not found. unable to determine zonepath" % fpath)
        with open(fpath, "r") as f:
            buff = f.read()
        for line in buff.split("\n"):
            if "set zonepath" in line:
                return line.split("=")[-1].strip()
        raise ex.excError("set zonepath command not found in %s" % fpath)

    def get_zonepath(self):
        if hasattr(self, "zonepath"):
            return self.zonepath
        try:
            zp = self.get_zonepath_from_zonecfg_cmd()
        except ex.excError:
            try:
                zp = self.get_zonepath_from_zonecfg_export()
            except ex.excError:
                zp = "/etc/system/%s" % self.name
        self.zonepath = zp
        return zp

    def files_to_sync(self):
        return [self.zone_cf]

    def zonecfg(self, zonecfg_args=[]):
        cmd = [ZONECFG, '-z', self.name] + zonecfg_args
        (ret, out, err) = self.vcall(cmd,err_to_info=True)
        if ret != 0:
            msg = '%s failed status: %i\n%s' % (" ".join(cmd), ret, out)
            self.log.error(msg)
            raise ex.excError(msg)
        else:
            msg = '%s done status: %i\n%s' % (" ".join(cmd), ret, out)
            self.log.info(msg)
        self.zone_refresh()
        return ret

    def zoneadm(self, action, option=None):
        if action in [ 'ready' , 'boot' ,'shutdown' , 'halt' ,'attach', 'detach', 'install', 'clone' ] :
            cmd = ['zoneadm', '-z', self.name, action ]
        else:
            self.log.error("unsupported zone action: %s" % action)
            return 1
        if option is not None:
            cmd += option

        t = datetime.now()
        (ret, out, err) = self.vcall(cmd,err_to_info=True)
        len = datetime.now() - t
        if ret != 0:
            msg = '%s failed status: %i in %s logs in %s' % (' '.join(cmd), ret, len, out)
            self.log.error(msg)
            raise ex.excError(msg)
        else:
            self.log.info('%s done in %s - ret %i - logs in %s'
                            % (' '.join(cmd), len, ret, out))
        self.zone_refresh()
        return ret

    def set_zonepath_perms(self):
        self.get_zonepath()
        if not os.path.exists(self.zonepath):
            os.makedirs(self.zonepath)
        s = os.stat(self.zonepath)
        if s.st_uid != 0 or s.st_gid != 0:
            self.log.info("set %s ownership to uid 0 gid 0"%self.zonepath)
            os.chown(self.zonepath, 0, 0)
        mode = s[ST_MODE]
        if (S_IWOTH&mode) or (S_IXOTH&mode) or (S_IROTH&mode) or \
           (S_IWGRP&mode) or (S_IXGRP&mode) or (S_IRGRP&mode):
            self.vcall(['chmod', '700', self.zonepath])

    def rcp_from(self, src, dst):
        self.get_zonepath()
        src = os.path.realpath(self.zonepath + '/root/' + src)
        cmd = ['cp', src, dst]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError("'%s' execution error:\n%s"%(' '.join(cmd), err))
        return out, err, ret

    def rcp(self, src, dst):
        self.get_zonepath()
        dst = os.path.realpath(self.zonepath + '/root/' + dst)
        cmd = ['cp', src, dst]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError("'%s' execution error:\n%s"%(' '.join(cmd), err))
        return out, err, ret

    def attach(self):
        self.zone_refresh()
        if self.state in ('installed' , 'ready', 'running'):
            self.log.info("zone container %s already installed" % self.name)
            return 0
        elif self.state is None:
            cmd = [ZONECFG, "-z", self.name, "-f", self.zone_cfg_path()]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                raise ex.excError
        try:
            self.umount_fs_in_zonepath()
            self.zoneadm('attach')
        except ex.excError:
            self.zoneadm('attach', ['-F'])
        self.can_rollback = True

    def delete(self):
        if not self.delete_on_stop:
            return 0
        self.zone_refresh()
        if self.state is None:
            self.log.info("zone container %s already deleted" % self.name)
            return 0
        cmd = [ZONECFG, "-z", self.name, "delete", "-F"]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError
        return 0

    def detach(self):
        self.zone_refresh()
        if self.state == "configured" :
            self.log.info("zone container %s already detached/configured" % self.name)
            return 0
        return self.zoneadm('detach')

    def ready(self):
        self.zone_refresh()
        if self.state == 'ready' or self.state == "running" :
            self.log.info("zone container %s already ready" % self.name)
            return 0
        self.set_zonepath_perms()
        return self.zoneadm('ready')

    def install_drp_flag(self):
        self.get_zonepath()
        flag = os.path.join(self.zonepath, ".drp_flag")
        self.log.info("install drp flag in container : %s"%flag)
        with open(flag, 'w') as f:
            f.write(' ')
            f.close()

    def get_smf_state(self, smf=None):
        cmd = self.runmethod + [SVCS, '-H', '-o', 'state', smf]
        (out, err, status) = justcall(cmd)
        if status == 0:
            return out.split('\n')[0]
        else:
            return False

    def is_smf_state(self, smf=None, value=None):
        current_value = self.get_smf_state(smf)
        if current_value is False:
            return False
        elif current_value == value:
            return True
        else:
            return False

    def is_multi_user(self):
        return self.is_smf_state(MULTI_USER_SMF, "online")

    def wait_multi_user(self):
        self.log.info("wait for smf state on on %s", MULTI_USER_SMF)
        self.wait_for_fn(self.is_multi_user, self.startup_timeout, 2)

    def boot(self):
        "return 0 if zone is running else return self.zoneadm('boot')"
        self.zone_refresh()
        if self.state == "running" :
            self.log.info("zone container %s already running" % self.name)
            return 0
        self.zoneadm('boot')
        if self.state == "running":
            return(0)
        else:
            raise(ex.excError("zone should be running"))
        self.log.info("wait for zone operational")
        self.wait_for_fn(self.operational, self.startup_timeout, 2)

    def halt(self):
        """ Need wait poststat after returning to installed state on ipkg
            example : /bin/ksh -p /usr/lib/brand/ipkg/poststate zonename zonepath 5 4
        """
        self.zone_refresh()
        if self.state in [ 'installed', 'configured'] :
            self.log.info("zone container %s already stopped" % self.name)
            return 0
        if self.state == 'running':
            (ret, out, err) = self.vcall(['zlogin' , self.name , '/sbin/init' , '0'])
            for t in range(self.shutdown_timeout):
                self.zone_refresh()
                if self.state == 'installed':
                    for t2 in range(self.shutdown_timeout):
                        time.sleep(1)
                        (out,err,st) = justcall([ 'pgrep', '-fl', 'ipkg/poststate.*'+ self.name])
                        if st == 0 :
                            self.log.info("Waiting for ipkg poststate complete: %s" % out)
                        else:
                            break
                    return 0
                time.sleep(1)
            self.log.info("timeout out waiting for %s shutdown", self.name)
        ret = self.zoneadm('halt')
        if ret != 0:
            return ret
        return 0

    def container_start(self):
        return self.boot()

    def _status(self, verbose=False):
        self.zone_refresh()
        if self.state == 'running' :
            return rcStatus.UP
        else:
            return rcStatus.DOWN

    def zone_refresh(self):
        """ refresh Zone object attributes:
                state
                zonepath
                brand
            from zoneadm -z zonename list -p
            zoneid:zonename:state:zonepath:uuid:brand:ip-type
        """

        (out,err,st) = justcall([ 'zoneadm', '-z', self.name, 'list', '-p' ])

        if st == 0 :
            out = out.strip()
            l = out.split(':')
            n_fields = len(l)
            if n_fields == 9:
                (zoneid,zonename,state,zonepath,uuid,brand,iptype,rw,macp) = l
            elif n_fields == 10:
                (zoneid,zonename,state,zonepath,uuid,brand,iptype,rw,macp,dummy) = l
            elif n_fields == 7:
                (zoneid,zonename,state,zonepath,uuid,brand,iptype) = l
            else:
                raise ex.excError("Unexpected zoneadm list output: %s"%out)
            if zonename == self.name :
                self.state = state
                self.zonepath = zonepath
                self.brand = brand
                return True
            else:
                return False
        else:
            return False

    def is_running(self):
        "return True if zone is running else False"
        self.zone_refresh()
        if self.state == 'running' :
            return True
        else:
            return False

    def is_up(self):
        "return self.is_running status"
        return self.is_running()

    def operational(self):
        "return status of: zlogin zone pwd"
        cmd = self.runmethod + ['pwd']
        if qcall(cmd) == 0:
            return True
        else:
            return False

    def boot_and_wait_reboot(self):
        """boot zone, then wait for automatic zone reboot
            boot zone
            wait for zone init process end
            wait for zone running
            wait for zone operational
        """
        self.log.info("wait for zone boot and reboot...")
        self.boot()
        if self.is_running is False:
            raise(ex.excError("zone is not running"))
        cmd = [PGREP, "-z", self.name, "-f", INIT]
        (out, err, st) = justcall(cmd)
        if st != 0:
            raise(ex.excError("fail to detect zone init process"))
        pids = " ".join(out.split("\n")).rstrip()
        cmd = [PWAIT, pids]
        self.log.info("wait for zone init process %s termination" % (pids))
        if qcall(cmd) != 0:
            raise(ex.excError("failed " + " ".join(cmd)))
        self.log.info("wait for zone running again")
        self.wait_for_fn(self.is_up, self.startup_timeout, 2)
        self.log.info("wait for zone operational")
        self.wait_for_fn(self.operational, self.startup_timeout, 2)

    def umount_fs_in_zonepath(self):
        """zone boot will fail if some fs linger under the zonepath.
           those fs might be datasets automounted upon zpool import.
           umount them.
           if they are needed, them still may be mounted by opensvc
           if declared as zoned fs or encap fs.
        """
        self.get_zonepath()
        if self.zonepath == "/":
            # sanity check
            return

        m = __import__('rcMounts'+rcEnv.sysname)
        mounts = m.Mounts()
        mounts.sort(reverse=True)
        mntpts = []
        for resource in self.svc.get_resources('fs'):
            mntpts.append(resource.mount_point)
        for mount in mounts.mounts:
            # don't unmount zonepath itself
            if mount.mnt == self.zonepath:
                continue
            if not mount.mnt.startswith(self.zonepath):
                continue
            # don't umount fs not handled by the service
            if mount.mnt not in mntpts:
                continue
            self.vcall(['umount', mount.mnt])
            self.vcall(['rmdir', mount.mnt])
            if mount.type == 'zfs':
                zfs_setprop(mount.dev, 'canmount', 'noauto')

    def start(self):
        if not 'noaction' in self.tags:
            self.attach()
            self.ready()
        self.svc.sub_set_action("ip", "start", tags=set([self.name]))
        if not 'noaction' in self.tags:
            self.boot()
        self.svc.sub_set_action([
            "disk.scsireserv",
            "disk.zpool",
            "disk.raw",
            "fs"], "start", tags=set([self.name]))

    def _stop(self):
        if not 'noaction' in self.tags:
            self.halt()
            self.detach()
            self.delete()

    def stop(self):
        self.export_zone_cfg()
        self.svc.sub_set_action([
            "fs",
            "disk.raw",
            "disk.zpool",
            "disk.scsireserv",
            "ip"], "stop", tags=set([self.name]))
        self._stop()

    def provision(self):
        if not 'noaction' in self.tags:
            resContainer.Container.provision()
        self.svc.sub_set_action([
            #"ip",
            "disk.scsireserv",
            "disk.zpool",
            "disk.raw",
            "fs"], "provision", tags=set([self.name]))

    def unprovision(self):
        self.svc.sub_set_action([
            "fs",
            "disk.raw",
            "disk.zpool",
            "disk.scsireserv",
            "ip"], "unprovision", tags=set([self.name]))
        if not 'noaction' in self.tags:
            resContainer.Container.unprovision()

    def presync(self):
        self.export_zone_cfg()

    def files_to_sync(self):
        return [self.zone_cfg_path()]

    def __str__(self):
        return "%s name=%s" % (Res.Resource.__str__(self), self.name)

    def get_container_info(self):
        vcpus = "0"
        vmem = "0"
        cmd = [ZONECFG, "-z", self.name, "info", "rctl", "name=zone.cpu-cap"]
        (out, err, status) = justcall(cmd)
        if status == 0:
            lines = out.split('\n')
            for line in lines:
                if "value:" not in line:
                    continue
                l = line.split("limit=")
                if len(l) == 2:
                    vcpus = l[-1][:l[-1].index(',')]
                    vcpus = str(float(vcpus)/100)
                    break

        cmd = [ZONECFG, "-z", self.name, "info", "capped-memory"]
        (out, err, status) = justcall(cmd)
        if status == 0:
            lines = out.split('\n')
            for line in lines:
                if "physical:" not in line:
                    continue
                l = line.split(": ")
                if len(l) == 2:
                    vmem = l[-1].strip()
                    if vmem.endswith('T'):
                        vmem = str(float(vmem[:-1])*1024*1024)
                    elif vmem.endswith('G'):
                        vmem = str(float(vmem[:-1])*1024)
                    elif vmem.endswith('M'):
                        vmem = vmem[:-1]
                    elif vmem.endswith('K'):
                        vmem = str(float(vmem[:-1])/1024)
                    break

        return {'vcpus': vcpus, 'vmem': vmem}


if __name__ == "__main__":
    for c in (Zone,) :
        help(c)

  0707010005c9af000081b40000000000000000000000015ab21754000006e0000000660000000000000000000000000000002600000000root/usr/share/opensvc/lib/resDisk.py """
Base disk resource driver module.
"""

import os
import resources as Res
import rcStatus
import rcExceptions as exc
from rcGlobalEnv import rcEnv

class Disk(Res.Resource):
    """
    Base disk resource driver, derived for LVM, Veritas, ZFS, ...
    """

    def __init__(self, rid=None, name=None, **kwargs):
        Res.Resource.__init__(self, rid, **kwargs)
        self.name = name

    def __str__(self):
        return "%s name=%s" % (Res.Resource.__str__(self), self.name)

    def has_it(self): return False
    def is_up(self): return False
    def do_start(self): return False
    def do_stop(self): return False

    def stop(self):
        self.do_stop()

    def start(self):
        self.promote_rw()
        self.do_start()

    def _status(self, verbose=False):
        if self.is_up():
            return rcStatus.UP
        else:
            return rcStatus.DOWN

    def create_static_name(self, dev, suffix="0"):
        d = self.create_dev_dir()
        lname = self.rid.replace("#", ".") + "." + suffix
        l = os.path.join(d, lname)
        if os.path.exists(l) and os.path.realpath(l) == dev:
            return
        self.log.info("create static device name %s -> %s" % (l, dev))
        try:
            os.unlink(l)
        except:
            pass
        os.symlink(dev, l)

    def create_dev_dir(self):
        d = os.path.join(self.svc.var_d, "dev")
        if not os.path.exists(d):
            os.makedirs(d, 0o755)
        return d

if __name__ == "__main__":
    for c in (Disk,) :
        help(c)

    print("""d=Disk("aGenericDisk")""")
    d=Disk("aGenericDisk")
    print("show d", d)
    print("""d.do_action("start")""")
    d.do_action("start")
    print("""d.do_action("stop")""")
    d.do_action("stop")
0707010005c9b0000081b40000000000000000000000015ab2175400001dee000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/resDiskAmazon.py   import resDisk
import os
import glob
import time
import rcStatus
import rcExceptions as ex
from rcGlobalEnv import *
from rcAmazon import Amazon

class Disk(resDisk.Disk, Amazon):
    def __init__(self,
                 rid=None,
                 type="disk.vg",
                 volumes=set(),
                 client_id=None,
                 keyring=None,
                 **kwargs):

        resDisk.Disk.__init__(self,
                          rid=rid,
                          type=type,
                          **kwargs)

        self.volumes = volumes
        self.label = self.fmt_label()
        self.existing_volume_ids = None
        self.mapped_bdevs = None
        self.dev_prefix = None

    def get_existing_volume_ids(self, refresh=False):
        if self.existing_volume_ids is not None and not refresh:
             return self.existing_volume_ids
        data = self.aws(["ec2", "describe-volumes", "--volume-ids"] + self.volumes, verbose=False)
        self.existing_volume_ids = [ b["VolumeId"] for b in data["Volumes"] ]
        return self.existing_volume_ids

    def get_state(self, vol):
        data = self.aws(["ec2", "describe-volumes", "--volume-ids", vol], verbose=False)
        try:
            avail = data["Volumes"][0]["State"]
        except:
            avail = "not present"
        return avail

    def wait_dev(self, dev):
        dev = self.mangle_devpath(dev)
        for i in range(60):
            if os.path.exists(dev):
                return
            self.log.info("%s is not present yet" % dev)
            time.sleep(1)
        raise ex.excError("timeout waiting for %s to appear." % dev)

    def wait_avail(self, vol):
        for i in range(30):
            state = self.get_state(vol)
            self.log.info("%s state: %s" % (vol, state))
            if state == "available":
                return
            time.sleep(1)
        raise ex.excError("timeout waiting for %s to become available. last %s" % (vol, state))

    def get_mapped_dev(self, volume):
        instance_data = self.get_instance_data(refresh=True)
        if instance_data is None:
            raise ex.excError("can't find instance data")

        devs = []
        for b in instance_data["BlockDeviceMappings"]:
            if b["Ebs"]["VolumeId"] != volume:
                continue
            return b["DeviceName"]

    def get_mapped_devs(self, volumes=None):
        instance_data = self.get_instance_data(refresh=True)
        if instance_data is None:
            raise ex.excError("can't find instance data")

        devs = []
        for b in instance_data["BlockDeviceMappings"]:
            if volumes is not None and b["Ebs"]["VolumeId"] not in volumes:
                continue
            try:
                devs.append(b["DeviceName"])
            except:
                pass
        return devs

    def get_next_dev(self):
        devs = self.get_mapped_devs()
        if (devs) == 0:
            return "/dev/sdb"
        devs = [ r.rstrip("0123456789") for r in devs ]
        devs = [ r.replace("/dev/sd", "") for r in devs ]
        devs += [ r.replace("/dev/sd", "") for r in glob.glob("/dev/sd[a-z]*") ]
        devs += [ r.replace("/dev/xvd", "") for r in glob.glob("/dev/xvd[a-z]*") ]
        chars = "abcdefghijklmnopqrstuvwxyz"
        for c in chars:
            if c not in devs:
                return "/dev/sd"+c
        for c in chars:
            for d in chars:
                if c+d not in devs:
                    return "/dev/sd"+c+d
        for c in chars:
            for d in chars:
                for e in chars:
                    if c+d+e not in devs:
                        return "/dev/sd"+c+d+e
        raise ex.excError("no available device name")

    def get_mapped_bdevs(self, refresh=False):
        if self.mapped_bdevs is not None and not refresh:
             return self.mapped_bdevs
        instance_data = self.get_instance_data(refresh=True)
        if instance_data is None:
            raise ex.excError("can't find instance data")

        self.mapped_bdevs = []
        for b in instance_data["BlockDeviceMappings"]:
            try:
                self.mapped_bdevs.append(b["Ebs"]["VolumeId"])
            except:
                pass
        return self.mapped_bdevs

    def fmt_label(self):
        s = "ec2 volumes "
        s += ", ".join(self.volumes)
        return s

    def has_it(self, volume):
        mapped = self.get_mapped_bdevs()
        if volume in mapped:
            return True
        return False

    def up_count(self):
        mapped = self.get_mapped_bdevs()
        l = []
        for volume in self.volumes:
            if volume in mapped:
                l.append(volume)
        return l

    def validate_volumes(self):
        existing_volumes = self.get_existing_volume_ids()
        non_exist = set(self.volumes) - set(existing_volumes)
        if len(non_exist) > 0:
            raise Exception("non allocated volumes: %s" % ', '.join(non_exist))

    def _status(self, verbose=False):
        try:
            self.validate_volumes()
        except Exception as e:
            self.status_log(str(e))
            return rcStatus.WARN
        l = self.up_count()
        n = len(l)
        unmapped = sorted(list(set(self.volumes) - set(l)))
        if n == len(self.volumes):
            return rcStatus.UP
        elif n == 0:
            return rcStatus.DOWN
        else:
            self.status_log("unattached: "+", ".join(unmapped))
            return rcStatus.DOWN

    def get_dev_prefix(self):
        if self.dev_prefix is not None:
            return self.dev_prefix
        if len(glob.glob("/dev/xvd*")) > 0:
            self.dev_prefix = "/dev/xvd"
        else:
            self.get_dev_prefix = "/dev/sd"
        return self.dev_prefix

    def mangle_devpath(self, dev):
        return dev.replace("/dev/sd", self.get_dev_prefix())

    def do_start_one(self, volume):
        mapped = self.get_mapped_bdevs()
        if volume in mapped:
            self.log.info(volume+" is already attached")
            dev = self.get_mapped_dev(volume)
        else:
            dev = self.get_next_dev()
            data = self.aws([
              "ec2", "attach-volume",
              "--instance-id", self.get_instance_id(),
              "--volume-id", volume,
              "--device", dev
            ])
            self.can_rollback = True
        self.wait_dev(dev)
        self._create_static_name(self.mangle_devpath(dev), volume)

    def _create_static_name(self, dev, volume):
        suffix = str(self.volumes.index(volume))
        self.create_static_name(dev, suffix)

    def do_start(self):
        self.validate_volumes()
        for volume in self.volumes:
            self.do_start_one(volume)
        self.get_mapped_bdevs(refresh=True)

    def do_stop_one(self, volume):
        mapped = self.get_mapped_bdevs()
        if volume not in mapped:
            self.log.info(volume+" is already detached")
            return
        data = self.aws([
          "ec2", "detach-volume",
          "--instance-id", self.get_instance_id(),
          "--volume-id", volume
        ])

    def do_stop(self):
        self.validate_volumes()
        for volume in self.volumes:
            self.do_stop_one(volume)
        self.get_mapped_bdevs(refresh=True)

    def exposed_devs(self):
        devs = self.get_mapped_devs(volumes=self.volumes)
        if len(devs) == 0:
            return set(devs)
        return set([ self.mangle_devpath(r) for r in devs ])

    def exposed_disks(self):
        disks = set([ r.rstrip("1234567890") for r in self.sub_devs() ])
        return disks

  0707010005c9b1000081b40000000000000000000000015ab2175400000f7c000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/resDiskDisk.py from __future__ import print_function

import json
import time

import resources as Res
from rcGlobalEnv import rcEnv
from rcUtilities import lazy
import rcExceptions as ex

class Disk(Res.Resource):
    """
    SAN Disk resource
    """

    def __init__(self, rid=None, disk_id=None, **kwargs):
        Res.Resource.__init__(self, rid, "disk.disk", **kwargs)
        self.disk_id = disk_id if disk_id != "" else None
        self.set_label()

    def set_label(self):
        if self.disk_id is None:
            self.label = "unprovisioned disk"
        else:
            self.label = "disk "+str(self.disk_id)

    def _info(self):
        return [
            ["disk_id", self.disk_id],
        ]

    def __str__(self):
        return "%s disk disk_id=%s" % (
            Res.Resource.__str__(self),
            str(self.disk_id),
        )

    @lazy
    def array_name(self):
        try:
            return self.svc.conf_get(self.rid, "array")
        except:
            raise ex.excError("disk %s: missing the 'array' provisioning parameter" % self.rid)

    @lazy
    def diskgroup(self):
        try:
            return self.svc.conf_get(self.rid, "diskgroup")
        except:
            raise ex.excError("disk %s: missing the 'diskgroup' provisioning parameter" % self.rid)

    @lazy
    def array_id(self):
        data = self.svc.collector_rest_get("/arrays", {
            "filters": "array_name "+self.array_name,
            "props": "id",
        })
        if "error" in data:
            raise ex.excError(data["error"])
        if data["meta"]["total"] != 1:
            raise ex.excError("array %s has %d matching candidates" % (self.array_name, data["meta"]["total"]))
        return data["data"][0]["id"]

    def get_form_id(self, form_name):
        data = self.svc.collector_rest_get("/forms", {
            "filters": "form_name "+form_name,
            "props": "id",
        })
        if "error" in data:
            raise ex.excError(data["error"])
        if data["meta"]["total"] != 1:
            raise ex.excError("form %s has %d matching candidates" % (form_name, data["meta"]["total"]))
        return data["data"][0]["id"]

    def wait_results(self, results):
        """
        Ask the collector for a submitted form-based action results
        until the action is completed.

        The results format is:
        {
            'status': 'QUEUED',
            'request_data': {},
            'returncode': 0,
            'log': {
                'output-0': [
                ],
            },
            'outputs': {
                'output-0': {
                },
            },
            'results_id': 300,
            'outputs_order': ['output-0']
        }
        """
        logs = {}

        while True:
            data = self.get_results(results)
            if "error" in data:
                raise ex.excError(data["error"])

            for output, log in data["log"].items():
                if output not in logs:
                    logs[output] = 0
                if len(log) > logs[output]:
                    for lvl, fmt, d in log[logs[output]:]:
                        if len(fmt) == 0:
                            continue
                        try:
                            msg = fmt % d
                        except:
                            msg = "corrupted collector request log line"
                        if lvl == 0:
                            self.log.info(msg)
                        else:
                            self.log.error(msg)
                    logs[output] = len(log)
            if data["status"] == "COMPLETED":
                if data["returncode"] != 0:
                    raise ex.excError("collector request completed with errors")
                return data
            time.sleep(1)

    def get_results(self, results):
        data = self.svc.collector_rest_get("/form_output_results/%d" % results["results_id"])
        return data


0707010005c9b2000081b40000000000000000000000015ab2175400000590000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/resDiskDiskLinux.py    from __future__ import print_function

import os
import time

import rcExceptions as ex
import resDiskDisk
from rcUtilities import lazy, which, justcall
from rcGlobalEnv import rcEnv
import rcStatus

class Disk(resDiskDisk.Disk):
    @lazy
    def devpath(self):
        return "/dev/disk/by-id/wwn-0x%s" % str(self.disk_id).lower().replace("0x", "")

    def _status(self, verbose=False):
        if self.disk_id is None:
            return rcStatus.NA
        if not os.path.exists(self.devpath):
            self.status_log("%s does not exist" % self.devpath, "warn")
            return rcStatus.DOWN
        return rcStatus.NA

    def exposed_devs(self):
        try:
            dev = os.path.realpath(self.devpath)
            return set([dev])
        except Exception as exc:
            print(exc)
            pass
        return set()

    def provision(self):
        resDiskDisk.Disk.provision(self)
        self.wait_udev()
        if which(rcEnv.syspaths.multipath):
            dev = os.path.realpath(self.devpath)
            cmd = [rcEnv.syspaths.multipath, "-v1", dev]
            ret, out, err = self.vcall(cmd)

    def wait_udev(self):
        for retry in range(30):
            if os.path.exists(self.devpath):
                self.log.info("%s now exists", self.devpath)
                return
            time.sleep(1)
        raise ex.excError("time out waiting for %s to appear" % self.devpath)

0707010005c9b3000081b40000000000000000000000015ab21754000018eb000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/resDiskDrbd.py import os
import resources as Res
import rcStatus
import rcExceptions as ex
from rcUtilities import which, justcall
from rcGlobalEnv import rcEnv

class Drbd(Res.Resource):
    """ Drbd device resource

        The tricky part is that drbd devices can be used as PV
        and LV can be used as drbd base devices. Beware of the
        the ordering deduced from rids and subsets.

        Start 'ups' and promotes the drbd devices to primary.
        Stop 'downs' the drbd devices.
    """

    def __init__(self,
                 rid=None,
                 res=None,
                 **kwargs):
        Res.Resource.__init__(self,
                              rid,
                              "disk.drbd",
                              **kwargs)
        self.res = res
        self.label = "drbd "+res
        self.drbdadm = None

    def __str__(self):
        return "%s resource=%s" % (Res.Resource.__str__(self),\
                                 self.res)

    def files_to_sync(self):
        cf = os.path.join(os.sep, 'etc', 'drbd.d', self.res+'.res')
        if os.path.exists(cf):
            return [cf]
        self.log.error("%s does not exist"%cf)
        return []

    def drbdadm_cmd(self, cmd):
        if self.drbdadm is None:
            if which('drbdadm'):
                self.drbdadm = 'drbdadm'
            else:
                self.log("drbdadm command not found")
                raise exc.excError
        return [self.drbdadm] + cmd.split() + [self.res]

    def exposed_devs(self):
        devps = set()

        (ret, out, err) = self.call(self.drbdadm_cmd('dump-xml'))
        if ret != 0:
            raise ex.excError

        from xml.etree.ElementTree import XML, fromstring
        tree = fromstring(out)

        for res in tree.getiterator('resource'):
            if res.attrib['name'] != self.res:
                continue
            for host in res.getiterator('host'):
                if host.attrib['name'] != rcEnv.nodename:
                    continue
                d = host.find('device')
                if d is None:
                    d = host.find('volume/device')
                if d is None:
                    continue
                devps |= set([d.text])
        return devps

    def sub_devs(self):
        devps = set()

        (ret, out, err) = self.call(self.drbdadm_cmd('dump-xml'))
        if ret != 0:
            raise ex.excError

        from xml.etree.ElementTree import XML, fromstring
        tree = fromstring(out)

        for res in tree.getiterator('resource'):
            if res.attrib['name'] != self.res:
                continue
            for host in res.getiterator('host'):
                if host.attrib['name'] != rcEnv.nodename:
                    continue
                d = host.find('disk')
                if d is None:
                    d = host.find('volume/disk')
                if d is None:
                    continue
                devps |= set([d.text])

        return devps

    def drbdadm_down(self):
        (ret, out, err) = self.vcall(self.drbdadm_cmd('down'))
        if ret != 0:
            raise ex.excError

    def drbdadm_up(self):
        (ret, out, err) = self.vcall(self.drbdadm_cmd('up'))
        if ret != 0:
            raise ex.excError

    def get_cstate(self):
        self.prereq()
        (out, err, ret) = justcall(self.drbdadm_cmd('cstate'))
        if ret != 0:
            if "Device minor not allocated" in err:
                return "Unattached"
            else:
                raise ex.excError
        return out.strip()

    def prereq(self):
        if not os.path.exists("/proc/drbd"):
            (ret, out, err) = self.vcall(['modprobe', 'drbd'])
            if ret != 0: raise ex.excError

    def start_connection(self):
        cstate = self.get_cstate()
        if cstate == "Connected":
            self.log.info("drbd resource %s is already up"%self.res)
        elif cstate == "StandAlone":
            self.drbdadm_down()
            self.drbdadm_up()
        elif cstate == "WFConnection":
            self.log.info("drbd resource %s peer node is not listening"%self.res)
            pass
        else:
            self.drbdadm_up()

    def get_roles(self):
        out, err, ret = justcall(self.drbdadm_cmd('role'))
        if ret != 0:
            raise ex.excError(err)
        out = out.strip().split('/')
        if len(out) != 2:
            raise ex.excError(out)
        return out

    def start_role(self, role):
        roles = self.get_roles()
        if roles[0] != role:
            (ret, out, err) = self.vcall(self.drbdadm_cmd(role.lower()))
            if ret != 0:
                raise ex.excError
        else:
            self.log.info("drbd resource %s is already %s"%(self.res, role))

    def startstandby(self):
        self.start_connection()
        roles = self.get_roles()
        if roles[0] == "Primary":
            return
        self.start_role('Secondary')
        self.can_rollback = True

    def stopstandby(self):
        self.start_connection()
        roles = self.get_roles()
        if roles[0] == "Secondary":
            return
        self.start_role('Secondary')

    def start(self):
        self.start_connection()
        self.start_role('Primary')
        self.can_rollback = True

    def stop(self):
        if self.standby:
            self.stopstandby()
        else:
            self.drbdadm_down()

    def _status(self, verbose=False):
        try:
            roles = self.get_roles()
        except Exception as e:
            self.status_log(str(e))
            return rcStatus.WARN
        self.status_log(str(roles[0]), "info")
        ret, out, err = self.call(self.drbdadm_cmd('dstate'))
        if ret != 0:
            self.status_log("drbdadm dstate %s failed"%self.res)
            return rcStatus.WARN
        out = out.strip()
        if out == "UpToDate/UpToDate":
            pass
        elif out == "Unconfigured":
            return rcStatus.DOWN
        else:
            self.status_log("unexpected drbd resource %s state: %s"%(self.res, out))
        if roles[0] == "Primary":
            return rcStatus.UP
        elif roles[0] == "Secondary" and self.standby:
            return rcStatus.STDBY_UP
        else:
            return rcStatus.WARN

if __name__ == "__main__":
    help(Drbd)
    v = Drbd(res='test')
    print(v)

 0707010005c9b4000081b40000000000000000000000015ab21754000017e5000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/resDiskGandi.py    import resDisk
import os
import rcStatus
import re
import pwd
import grp
import stat
from rcGlobalEnv import rcEnv
from rcUtilities import is_string
import rcExceptions as ex

class Disk(resDisk.Disk):
    def __init__(self,
                 rid=None,
                 name=None,
                 node=None,
                 cloud_id=None,
                 user="root",
                 group="root",
                 perm="660",
                 **kwargs):
        resDisk.Disk.__init__(self,
                          rid=rid,
                          name="gandi",
                          type='disk.gandi',
                          **kwargs)

        self.label = "gandi volume %s"%str(name)
        self.name = name
        self.node = node
        self.cloud_id = cloud_id
        self.user = user
        self.group = group
        self.perm = perm

        self.get_uid()
        self.get_gid()

    def print_obj(self, n):
        for k in dir(n):
            if '__' in k:
                continue
            print(k, "=", getattr(n, k))

    def get_cloud(self):
        if hasattr(self, 'cloud'):
            return self.cloud
        try:
            self.cloud = self.svc.node.cloud_get(self.cloud_id)
        except ex.excInitError as e:
            raise ex.excError(str(e))
        return self.cloud

    def get_uid(self):
        self.uid = self.user
        if is_string(self.uid):
            try:
                info=pwd.getpwnam(self.uid)
                self.uid = info[2]
            except:
                pass

    def get_gid(self):
        self.gid = self.group
        if is_string(self.gid):
            try:
                info=grp.getgrnam(self.gid)
                self.gid = info[2]
            except:
                pass

    def check_uid(self, rdev, verbose=False):
        if not os.path.exists(rdev):
            return True
        uid = os.stat(rdev).st_uid
        if uid != self.uid:
            if verbose:
                self.status_log('%s uid should be %d but is %d'%(rdev, self.uid, uid))
            return False
        return True

    def check_gid(self, rdev, verbose=False):
        if not os.path.exists(rdev):
            return True
        gid = os.stat(rdev).st_gid
        if gid != self.gid:
            if verbose:
                self.status_log('%s gid should be %d but is %d'%(rdev, self.gid, gid))
            return False
        return True

    def check_perm(self, rdev, verbose=False):
        if not os.path.exists(rdev):
            return True
        try:
            perm = oct(stat.S_IMODE(os.stat(rdev).st_mode))
        except:
            self.log.error('%s can not stat file'%rdev)
            return False
        perm = str(perm).lstrip("0o").lstrip("0")
        if perm != str(self.perm):
            if verbose:
                self.status_log('%s perm should be %s but is %s'%(rdev, str(self.perm), perm))
            return False
        return True

    def has_it(self):
        """Returns True if all devices are present
        """
        try:
            node = self.get_node()
        except ex.excError as e:
            raise ex.excError("can't find cloud node to list volumes (%s)"%str(e))

        c = self.get_cloud()
        disks = c.driver._node_info(node.id)['disks']
        for disk in disks:
            if disk['name'] == self.name:
                return True
        return False

    def is_up(self):
        """Returns True if the volume group is present and activated
        """
        return self.has_it()

    def _status(self, verbose=False):
        try:
            s = self.is_up()
        except ex.excError as e:
            self.status_log(str(e))
            return rcStatus.WARN
        if s:
            return rcStatus.UP
        else:
            return rcStatus.DOWN

    def get_node(self):
        c = self.get_cloud()
        if self.node is not None:
            n = self.node
        else:
            n = rcEnv.nodename
        try:
            nodes = c.driver.list_nodes()
        except Exception as e:
            raise ex.excError(str(e))
        for node in nodes:
            if node.name == n:
                return node
        raise ex.excError()

    def get_disk(self):
        c = self.get_cloud()
        disks = c.driver.ex_list_disks()
        _disk = None
        for disk in disks:
            if disk.name == self.name:
                _disk = disk
        if _disk is None:
            raise ex.excError()
        return _disk

    def do_start(self):
        try:
            node = self.get_node()
        except ex.excError as e:
            raise ex.excError("can't find cloud node to attach volume %s to (%s)"%(self.name, str(e)))

        try:
            disk = self.get_disk()
        except:
            raise ex.excError("volume %s not found in %s"%(self.name, self.cloud_id))

        try:
            status = self.is_up()
        except ex.excError as e:
            self.log.error("abort gandi volume %s attach: %s"%(self.name, str(e)))

        if status:
            self.log.info("gandi volume %s is already attached"%self.name)
            return

        self.log.info("attach gandi volume %s"%self.name)
        c = self.get_cloud()
        c.driver.ex_node_attach_disk(node, disk)
        self.can_rollback = True

    def do_stop(self):
        try:
            node = self.get_node()
        except ex.excError as e:
            raise ex.excError("can't find cloud node to detach volume %s from"%(self.name, str(e)))

        try:
            disk = self.get_disk()
        except:
            raise ex.excError("volume %s not found in %s"%(self.name, self.cloud_id))

        try:
            status = self.is_up()
        except ex.excError as e:
            self.log.error("abort gandi volume %s detach: %s"%(self.name, str(e)))

        if not status:
            self.log.info("gandi volume %s is already detached"%self.name)
            return

        self.log.info("detach gandi volume %s"%self.name)
        c = self.get_cloud()
        c.driver.ex_node_detach_disk(node, disk)

    def shutdown(self):
        pass

   0707010005c9b5000081b40000000000000000000000015ab217540000163f000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/resDiskGce.py  import resDisk
import os
import json
import rcStatus
import rcExceptions as ex
from rcGlobalEnv import *
from rcUtilities import justcall
import rcGce

class Disk(resDisk.Disk, rcGce.Gce):
    def __init__(self,
                 rid=None,
                 type="disk.gce",
                 names=set(),
                 gce_zone=None,
                 **kwargs):

        resDisk.Disk.__init__(self,
                          rid=rid,
                          type=type,
                          **kwargs)

        self.names = names
        self.gce_zone = gce_zone
        self.label = self.fmt_label()

    def get_disk_names(self, refresh=False):
        data = self.get_disks(refresh=refresh)
        return [d["name"] for d in data]

    def get_attached_disk_names(self, refresh=False):
        data = self.get_attached_disks(refresh=refresh)
        return [d["name"] for d in data]

    def get_attached_disks(self, refresh=False):
        if hasattr(self.svc, "gce_attached_disks") and not refresh:
             return self.svc.gce_attached_disks
        self.wait_gce_auth()
        cmd = ["gcloud", "compute", "instances", "describe", rcEnv.nodename, "--format", "json", "--zone", self.gce_zone]
        out, err, ret = justcall(cmd)
        data = json.loads(out)
        data = data.get("disks", [])
        for i, d in enumerate(data):
            data[i]["name"] = d["source"].split("/")[-1]
        self.svc.gce_attached_disks = data
        return self.svc.gce_attached_disks

    def get_disks(self, refresh=False):
        if hasattr(self.svc, "gce_disks") and not refresh:
             return self.svc.gce_disks
        self.wait_gce_auth()
        cmd = ["gcloud", "compute", "disks", "list", "--format", "json", "--zone", self.gce_zone]
        out, err, ret = justcall(cmd)
        data = json.loads(out)
        self.svc.gce_disks = data
        return data

    def fmt_label(self):
        s = "gce volumes "
        s += ", ".join(self.names)
        return s

    def has_it(self, name):
        data = self.get_attached_disks()
        disk_names = [d.get("name") for d in data]
        if name in disk_names:
            return True
        return False

    def up_count(self):
        data = self.get_attached_disks()
        disk_names = [d.get("name") for d in data]
        l = []
        for name in self.names:
            if name in disk_names:
                l.append(name)
        return l

    def validate_volumes(self):
        existing = [d.get("name") for d in self.get_disks()]
        non_exist = set(self.names) - set(existing)
        if len(non_exist) > 0:
            raise Exception("non allocated volumes: %s" % ', '.join(non_exist))

    def _status(self, verbose=False):
        try:
            self.validate_volumes()
        except Exception as e:
            self.status_log(str(e))
            return rcStatus.WARN
        l = self.up_count()
        n = len(l)
        unattached = sorted(list(set(self.names) - set(l)))
        if n == len(self.names):
            return rcStatus.UP
        elif n == 0:
            return rcStatus.DOWN
        else:
            self.status_log("unattached: "+", ".join(unattached))
            return rcStatus.DOWN

    def detach_other(self, name):
        existing = self.get_disks()
        for d in existing:
            if d["name"] != name:
                continue
            for user in d.get("users", []):
                instance = user.split('/')[-1]
                if instance != rcEnv.nodename:
                    self.vcall([
                      "gcloud", "compute", "instances", "detach-disk", "-q",
                      instance,
                      "--disk", name,
                      "--zone", self.gce_zone
                    ])

    def do_start_one(self, name):
        existing = self.get_disk_names()
        if name not in existing:
            self.log.info(name+" does not exist")
            return
        attached = self.get_attached_disk_names()
        if name in attached:
            self.log.info(name+" is already attached")
            return

        self.detach_other(name)
        self.vcall([
          "gcloud", "compute", "instances", "attach-disk", "-q",
          rcEnv.nodename,
          "--disk", name,
          "--zone", self.gce_zone,
          "--device-name", self.fmt_disk_devname(name),
        ])
        self.can_rollback = True

    def do_start(self):
        for name in self.names:
            self.do_start_one(name)
        self.get_attached_disks(refresh=True)

    def do_stop_one(self, name):
        existing = self.get_disk_names()
        if name not in existing:
            self.log.info(name+" does not exist")
            return
        attached = self.get_attached_disk_names()
        if name not in attached:
            self.log.info(name+" is already detached")
            return
        self.vcall([
          "gcloud", "compute", "instances", "detach-disk", "-q",
          rcEnv.nodename,
          "--disk", name,
          "--zone", self.gce_zone
        ])

    def do_stop(self):
        for name in self.names:
            self.do_stop_one(name)
        self.get_attached_disks(refresh=True)

    def fmt_disk_devname(self, name):
        index = self.names.index(name)
        return ".".join([self.svc.svcname, self.rid.replace("#", "."), str(index)])

    def exposed_devs(self):
        attached = self.get_attached_disks()
        return set(["/dev/disk/by-id/google-"+d["deviceName"] for d in attached if d["name"] in self.names])

    def exposed_disks(self):
        attached = self.get_attached_disks()
        return set([d["deviceName"] for d in attached if d["name"] in self.names])

 0707010005c9cb000081b40000000000000000000000015ab2175400001d49000000660000000000000000000000000000002400000000root/usr/share/opensvc/lib/resFs.py   import os
import subprocess
import time

import resources as Res
import rcExceptions as ex
import rcStatus
from rcGlobalEnv import rcEnv
from rcUtilities import which, mimport, lazy

class Mount(Res.Resource):
    """Define a mount resource
    """

    def __init__(self,
                 rid=None,
                 mount_point=None,
                 device=None,
                 fs_type=None,
                 mount_options=None,
                 snap_size=None,
                 **kwargs):
        Res.Resource.__init__(self,
                              rid=rid,
                              type="fs",
                              **kwargs)
        self.mount_point = mount_point
        self._device = device
        self.fs_type = fs_type
        self.mount_options = mount_options
        self.snap_size = snap_size
        self.fsck_h = {}
        self.netfs = ['nfs', 'nfs4', 'cifs', 'smbfs', '9pfs', 'gpfs', 'afs', 'ncpfs']

    @lazy
    def testfile(self):
        if not self.mount_point:
            return
        return os.path.join(self.mount_point, '.opensvc')

    def set_fsck_h(self):
        """
        Placeholder
        """
        pass

    @lazy
    def device(self):
        if self._device is not None:
            return self._device
        # lazy ref support, like {<rid>.exposed_devs[<n>]}
        return self.conf_get("dev")

    @lazy
    def label(self):
        if self.device is None:
            label = self.svc._get(self.rid+".dev", evaluate=False)
        else:
            label = self.device
        if self.mount_point is not None:
            label += "@" + self.mount_point
        if self.fs_type not in ("none", None):
            label = self.fs_type + " " + label
        return label

    def _info(self):
        data = [
          ["dev", self.device],
          ["mnt", self.mount_point],
          ["mnt_opt", self.mount_options if self.mount_options else ""],
        ]
        return data

    def start(self):
        self.validate_dev()
        self.promote_rw()
        self.create_mntpt()

    def validate_dev(self):
        if self.fs_type in ["zfs", "advfs"] + self.netfs:
            return
        if self.device == "none":
            # pseudo fs have no dev
            return
        if self.device.startswith("UUID=") or self.device.startswith("LABEL="):
            return
        if not os.path.exists(self.device):
            raise ex.excError("device does not exist %s" % self.device)

    def create_mntpt(self):
        if self.fs_type in ["zfs", "advfs"]:
            return
        if os.path.exists(self.mount_point):
            return
        try:
            os.makedirs(self.mount_point)
            self.log.info("create missing mountpoint %s" % self.mount_point)
        except:
            self.log.warning("failed to create missing mountpoint %s" % self.mount_point)

    def fsck(self):
        if self.fs_type in ("", "none") or os.path.isdir(self.device):
            # bind mounts are in this case
            return
        self.set_fsck_h()
        if self.fs_type not in self.fsck_h:
            self.log.debug("no fsck method for %s"%self.fs_type)
            return
        bin = self.fsck_h[self.fs_type]['bin']
        if which(bin) is None:
            self.log.warning("%s not found. bypass."%self.fs_type)
            return
        if 'reportcmd' in self.fsck_h[self.fs_type]:
            cmd = self.fsck_h[self.fs_type]['reportcmd']
            (ret, out, err) = self.vcall(cmd, err_to_info=True)
            if ret not in self.fsck_h[self.fs_type]['reportclean']:
                return
        cmd = self.fsck_h[self.fs_type]['cmd']
        (ret, out, err) = self.vcall(cmd)
        if 'allowed_ret' in self.fsck_h[self.fs_type]:
            allowed_ret = self.fsck_h[self.fs_type]['allowed_ret']
        else:
            allowed_ret = [0]
        if ret not in allowed_ret:
            raise ex.excError

    def need_check_writable(self):
        if 'ro' in self.mount_options.split(','):
            return False
        if self.fs_type in self.netfs:
            return False
        return True

    def can_check_writable(self):
        """ orverload in child classes to check os-specific conditions
            when a write test might hang (solaris lockfs, linux multipath
            with queueing on and no active path)
        """
        return True

    def check_stat(self):
        if which("stat") is None:
            return True

        if self.device is None:
            return True

        proc = subprocess.Popen(['stat', self.device],
                                stderr=subprocess.PIPE,
                                stdout=subprocess.PIPE)
        for retry in range(50, 0, -1):
            if proc.poll() is None:
                time.sleep(0.1)
            else:
                return True
        try:
            proc.kill()
        except OSError:
            pass
        return False

    def check_writable(self):
        if self.testfile is None:
            return True
        if not self.can_check_writable():
            return False

        try:
            f = open(self.testfile, 'w')
            f.write(' ')
            f.close()
        except IOError as e:
            if e.errno == 28:
                self.log.error('No space left on device. Invalidate writable test.')
                return True
            return False
        except:
            return False

        return True

    def _status(self, verbose=False):
        if self.is_up():
            if not self.check_stat():
                self.status_log("fs is not responding to stat")
                return rcStatus.WARN
            if self.need_check_writable() and not self.check_writable():
                self.status_log("fs is not writable")
                return rcStatus.WARN
            return rcStatus.UP
        else:
            return rcStatus.DOWN

    def sub_devs(self):
        pseudofs = [
          'lofs',
          'none',
          'proc',
          'sysfs',
        ]
        if self.fs_type in pseudofs + self.netfs:
            return set()
        for res in self.svc.get_resources():
            if hasattr(res, "is_child_dev") and res.is_child_dev(self.device):
                # don't account fs device if the parent resource is driven by the service
                return set()
        return set([self.device])

    def __str__(self):
        return "%s mnt=%s dev=%s fs_type=%s mount_options=%s" % (Res.Resource.__str__(self),\
                self.mount_point, self.device, self.fs_type, self.mount_options)

    def __lt__(self, other):
        """
        Order so that deepest mountpoint can be umount first.
        If no ordering constraint, honor the rid order.
        """
        smnt = os.path.dirname(self.mount_point)
        omnt = os.path.dirname(other.mount_point)
        if smnt == omnt:
            return Res.Resource.__lt__(self, other)
        return self.mount_point < other.mount_point

    @lazy
    def prov(self):
        try:
            mod = mimport("prov", "fs", self.fs_type, fallback=True)
        except ImportError:
            return
        if not hasattr(mod, "Prov"):
            raise ex.excError("missing Prov class in module %s" % str(mod))
        return getattr(mod, "Prov")(self)


if __name__ == "__main__":
    for c in (Mount,) :
        help(c)
    print("""   m=Mount("/mnt1","/dev/sda1","ext3","rw")   """)
    m=Mount("/mnt1","/dev/sda1","ext3","rw")
    print("show m", m)


   0707010005c9b6000081b40000000000000000000000015ab217540000113b000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/resDiskHpVm.py import re
import os
import rcExceptions as ex
import rcStatus
resVg = __import__("resDiskVgHP-UX")
from subprocess import *
from rcUtilities import qcall
from rcGlobalEnv import rcEnv
from subprocess import *

class Disk(resVg.Disk):
    def __init__(self,
                 rid=None,
                 name=None,
                 container_name=None,
                 **kwargs):
        resVg.Disk.__init__(self,
                          rid=rid,
                          name=name,
                          type='disk.vg',
                          **kwargs)
        self.label = "vmdg "+str(name)
        self.container_name = container_name

    def has_it(self):
        return True

    def is_up(self):
        return True

    def _status(self, verbose=False):
        return rcStatus.NA

    def do_start(self):
        self.do_mksf()

    def do_stop(self):
        pass

    def files_to_sync(self):
        return [self.sharefile_name(), self.mksffile_name()]

    def postsync(self):
        s = self.svc.group_status(excluded_groups=set(["sync", "hb"]))
        if s['overall'].status != rcStatus.UP:
            self.do_mksf()
            self.do_share()

    def presync(self):
        s = self.svc.group_status(excluded_groups=set(["sync", "hb"]))
        if self.svc.options.force or s['overall'].status == rcStatus.UP:
            self.write_mksf()
            self.write_share()

    def sharefile_name(self):
        return os.path.join(self.var_d, 'share')

    def get_devs(self):
        cmd = ['/opt/hpvm/bin/hpvmdevmgmt', '-l', 'all']
        (ret, buff, err) = self.call(cmd)
        if ret != 0:
            raise ex.excError
        if len(buff) == 0:
            return []
        a = {}
        for line in buff.split('\n'):
            if len(line) == 0:
                continue
            if "DEVTYPE=FILE" not in line and "DEVTYPE=DISK" not in line:
                continue
            if "SHARE=YES" in line:
                share = "YES"
            else:
                share = "NO"
            devs = line.split(":")[0]
            for dev in devs.split(","):
                a[dev] = {'share': share}
        return a

    def write_share(self):
        devs = self.get_devs()
        sub_devs = self.sub_devs()
        with open(self.sharefile_name(), 'w') as f:
            for dev in devs:
                if dev not in sub_devs:
                    continue
                f.write("%s:%s\n"%(dev, devs[dev]['share']))

    def do_share(self):
        if not os.path.exists(self.sharefile_name()):
            return
        devs = self.get_devs()
        errors = 0
        with open(self.sharefile_name(), 'r') as f:
            for line in f.readlines():
                l = line.split(':')
                if len(l) != 2:
                    continue
                dev = l[0]
                share = l[1].strip()
                if len(dev) == 0:
                    continue
                if not os.path.exists(dev):
                    continue
                if dev not in devs:
                    cmd = ['/opt/hpvm/bin/hpvmdevmgmt', '-a', 'gdev:'+dev]
                    (ret, out, err) = self.vcall(cmd)
                    if ret != 0:
                        self.log.error("error adding device %s hpvm device table"%dev)
                        raise ex.excError
                if dev in devs and share == devs[dev]['share']:
                    self.log.debug("skip set sharing of %s: already set to %s"%(dev, devs[dev]['share']))
                    continue
                cmd = ['/opt/hpvm/bin/hpvmdevmgmt', '-m', 'gdev:'+dev+':attr:SHARE='+share]
                (ret, buff, err) = self.vcall(cmd)
                if ret != 0:
                    self.log.error("error setting the shared attribute for %s"%dev)
                    errors += 1
                    continue
        if errors > 0:
            raise ex.excError

    def sub_devs(self):
        cmd = ['/opt/hpvm/bin/hpvmstatus', '-d', '-P', self.container_name]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE, close_fds=True)
        buff = p.communicate()
        if p.returncode != 0:
            raise ex.excError

        devs = set()
        for line in buff[0].split('\n'):
            l = line.split(':')
            if len(l) < 5:
                continue
            if l[3] != 'disk':
                continue
            devs |= set([l[4]])
        return devs
 0707010005c9b7000081b40000000000000000000000015ab2175400000d6f000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/resDiskLdom.py import os
import rcExceptions as ex
import rcStatus
m = __import__("resDisk")
from subprocess import *
import re

class Disk(m.Disk):
    def __init__(self,
                 rid=None,
                 name=None,
                 container_id=None,
                 **kwargs):
        self.label = "vmdg "+str(name)
        self.container_id = container_id
        m.Disk.__init__(self,
                          rid=rid,
                          name=name,
                          type='disk.vg',
                          **kwargs)

    def has_it(self):
        return True

    def is_up(self):
        return True

    def _status(self, verbose=False):
        return rcStatus.NA

    def do_start(self):
        pass

    def do_stop(self):
        pass

    def sub_devs(self):
        return self.exposed_devs()

    def exposed_devs(self):
        """
            VCC|name=vccname|...
            VDS|name=vdsname|...
            |vol=volname|..|dev=/dev/...|....
            |vol=volname1|..|dev=/dev/...|....
            VDS|name=vdsname1|..
            |vol=volname2|..|dev=/dev/...|....

            ldm list -o disk -p domname
            VERSION
            DOMAIN|..
            VDISK|name=...|vol=volname@vds|...
            VDISK|name=...|vol=volname2@vds2|...
        """
        vdevname2dev = {}
        devs = set()

        cmd = [ '/usr/sbin/ldm', 'list-services' , '-p' ]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE, close_fds=True)
        buff = p.communicate()
        if p.returncode != 0:
            raise ex.excError
        vds = ''
        for line in buff[0].split('\n'):
            keys = line.split('|')
            if keys[0] == 'VDS' and len(keys) > 1 :
                for k in keys :
                    name_value = k.split('=')
                    if name_value[0] == 'name' and len(name_value) == 2 :
                        vds = name_value[1]
            elif vds != '' and keys[0] == '':
                volname = ''
                dev = ''
                for k in keys :
                    name_value = k.split('=')
                    if name_value[0] == 'vol' and len(name_value) == 2 :
                        volname = name_value[1]
                    elif name_value[0] == 'dev' and len(name_value) == 2 :
                        dev = name_value[1]
                        if re.match('^/dev/dsk/', dev) is None:
                            continue
                        dev = dev.replace('/dev/dsk/','/dev/rdsk/',1)
                        if re.match('^.*s[0-9]$',dev) is None:
                            dev = dev + 's2'
                        vdevname2dev[volname + '@' + vds ] = dev
            else:
                vds = ''

        cmd = ['/usr/sbin/ldm', 'list', '-o', 'disk', '-p',
               self.svc.resources_by_id[self.container_id].name]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE, close_fds=True)
        buff = p.communicate()
        if p.returncode != 0:
            raise ex.excError
        for line in buff[0].split('\n'):
            keys = line.split('|')
            if keys[0] == 'VDISK' and len(keys) > 1 :
                for k in keys :
                    name_value = k.split('=')
                    if name_value[0] == 'vol' and len(name_value) == 2 :
                        vol = name_value[1]
                        if vol in vdevname2dev:
                            devs |= set([ vdevname2dev[vol] ])
        return devs
 0707010005c9b8000081b40000000000000000000000015ab2175400000238000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/resDiskLoop.py import resources as Res
from rcGlobalEnv import rcEnv

class Disk(Res.Resource):
    """
    Base loopback device resource
    """

    def __init__(self, rid=None, loopFile=None, **kwargs):
        Res.Resource.__init__(self, rid, "disk.loop", **kwargs)
        self.loopFile = loopFile
        self.label = "loop "+loopFile

    def _info(self):
        return [["file", self.loopFile]]

    def __str__(self):
        return "%s loopfile=%s" % (Res.Resource.__str__(self),\
                                 self.loopFile)

if __name__ == "__main__":
    help(Disk)
0707010005c9b9000081b40000000000000000000000015ab21754000005ff000000660000000000000000000000000000003000000000root/usr/share/opensvc/lib/resDiskLoopDarwin.py   import os
import re

from rcGlobalEnv import *
from rcUtilities import call, which
import rcStatus
import resDiskLoop as Res
import rcExceptions as ex
from rcLoopDarwin import file_to_loop

class Disk(Res.Disk):
    def is_up(self):
        """Returns True if the loop group is present and activated
        """
        self.loop = file_to_loop(self.loopFile)
        if len(self.loop) == 0:
            return False
        return True

    def start(self):
        if self.is_up():
            self.log.info("%s is already up" % self.loopFile)
            return
        cmd = ['hdiutil', 'attach', '-imagekey', 'diskimage-class=CRawDiskImage', '-nomount', self.loopFile]
        (ret, out, err) = self.call(cmd, info=True, outlog=False)
        if ret != 0:
            raise ex.excError
        self.loop = file_to_loop(self.loopFile)
        self.log.info("%s now loops to %s" % (', '.join(self.loop), self.loopFile))
        self.can_rollback = True

    def stop(self):
        if not self.is_up():
            self.log.info("%s is already down" % self.loopFile)
            return 0
        for loop in self.loop:
            cmd = ['hdiutil', 'detach', loop.strip('md')]
            (ret, out, err) = self.vcall(cmd)
            if ret != 0:
                raise ex.excError

    def _status(self, verbose=False):
        if self.is_up():
            return rcStatus.UP
        else:
            return rcStatus.DOWN

    def __init__(self, rid, loopFile, **kwargs):
        Res.Disk.__init__(self, rid, loopFile, **kwargs)

 0707010005c9ba000081b40000000000000000000000015ab2175400000872000000660000000000000000000000000000003100000000root/usr/share/opensvc/lib/resDiskLoopFreeBSD.py  import os
import re

from rcGlobalEnv import *
from rcUtilities import call, which
import rcStatus
import resDiskLoop as Res
import rcExceptions as ex

def file_to_loop(f):
    """Given a file path, returns the loop device associated. For example,
    /path/to/file => /dev/loop0
    """
    if which('mdconfig') is None:
        return []
    if not os.path.isfile(f):
        return []
    (ret, out, err) = call(['mdconfig', '-l', '-v'])
    if ret != 0:
        return []
    """ It's possible multiple loopdev are associated with the same file
    """
    devs= []
    for line in out.split('\n'):
        l = line.split()
        if len(l) < 4:
            continue
        path = ' '.join(l[3:])
        if path != f:
            continue
        if not os.path.exists('/dev/'+l[0]):
            continue
        devs.append(l[0])
    return devs

class Disk(Res.Disk):
    def is_up(self):
        """Returns True if the loop group is present and activated
        """
        self.loop = file_to_loop(self.loopFile)
        if len(self.loop) == 0:
            return False
        return True

    def start(self):
        if self.is_up():
            self.log.info("%s is already up" % self.loopFile)
            return
        cmd = ['mdconfig', '-a', '-t', 'vnode', '-f', self.loopFile]
        (ret, out, err) = self.call(cmd, info=True, outlog=False)
        if ret != 0:
            raise ex.excError
        self.loop = file_to_loop(self.loopFile)
        self.log.info("%s now loops to %s" % (', '.join(self.loop), self.loopFile))
        self.can_rollback = True

    def stop(self):
        if not self.is_up():
            self.log.info("%s is already down" % self.loopFile)
            return 0
        for loop in self.loop:
            cmd = ['mdconfig', '-d', '-u', loop.strip('md')]
            (ret, out, err) = self.vcall(cmd)
            if ret != 0:
                raise ex.excError

    def _status(self, verbose=False):
        if self.is_up():
            return rcStatus.UP
        else:
            return rcStatus.DOWN

    def __init__(self, rid, loopFile, **kwargs):
        Res.Disk.__init__(self, rid, loopFile, **kwargs)
  0707010005c9bb000081b40000000000000000000000015ab2175400000920000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/resDiskLoopLinux.py    import os
import re
import time

from rcGlobalEnv import *
from rcUtilities import call, which
import rcStatus
import resDiskLoop as Res
import rcExceptions as ex
from rcLoopLinux import file_to_loop
from lock import cmlock

class Disk(Res.Disk):
    def is_up(self):
        """Returns True if the loop group is present and activated
        """
        self.loop = file_to_loop(self.loopFile)
        if len(self.loop) == 0:
            return False
        return True

    def start(self):
        lockfile = os.path.join(rcEnv.paths.pathlock, "disk.loop")
        if self.is_up():
            self.log.info("%s is already up" % self.label)
            return
        try:
            with cmlock(timeout=30, delay=1, lockfile=lockfile):
                cmd = [rcEnv.syspaths.losetup, '-f', self.loopFile]
                (ret, out, err) = self.vcall(cmd)
        except Exception as exc:
            raise ex.excError(str(exc))
        if ret != 0:
            raise ex.excError
        self.loop = file_to_loop(self.loopFile)
        if len(self.loop) == 0:
            raise ex.excError("loop device did not appear or disappeared")
        time.sleep(2)
        self.log.info("%s now loops to %s" % (', '.join(self.loop), self.loopFile))
        self.can_rollback = True

    def stop(self):
        if not self.is_up():
            self.log.info("%s is already down" % self.label)
            return 0
        for loop in self.loop:
            cmd = [rcEnv.syspaths.losetup, '-d', loop]
            (ret, out, err) = self.vcall(cmd)
            if ret != 0:
                raise ex.excError

    def resource_handling_file(self):
        path = os.path.dirname(self.loopFile)
        return self.svc.resource_handling_dir(path)

    def _status(self, verbose=False):
        r = self.resource_handling_file()
        if not os.path.exists(self.loopFile):
            if r is None or (r and r.status() in (rcStatus.UP, rcStatus.STDBY_UP)):
                self.status_log("%s does not exist" % self.loopFile)
        if self.is_up():
            return rcStatus.UP
        else:
            return rcStatus.DOWN

    def __init__(self, rid, loopFile, **kwargs):
        Res.Disk.__init__(self, rid, loopFile, **kwargs)

    def exposed_devs(self):
        self.loop = file_to_loop(self.loopFile)
        return set(self.loop)

0707010005c9bc000081b40000000000000000000000015ab2175400000228000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/resDiskLoopSunOS.py    import resources
from rcGlobalEnv import *
import rcStatus

class Loop(resources.Resource):

    def is_up(self):
        """
        Returns True if the volume group is present and activated
        """
        return True

    def start(self):
        pass

    def stop(self):
        pass

    def status(self, verbose=False):
        if self.is_up():
            return rcStatus.UP
        else:
            return rcStatus.DOWN

    def __init__(self, file, **kwargs):
        resources.Resource.__init__(self, **kwargs)
        self.file = file
0707010005c9bd000081b40000000000000000000000015ab2175400000fc9000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/resDiskLvLinux.py  import re
import os
import rcExceptions as ex
import resDisk
from rcGlobalEnv import rcEnv
from rcUtilitiesLinux import major, get_blockdev_sd_slaves, \
                             devs_to_disks, udevadm_settle
from rcUtilities import which, justcall, cache

class Disk(resDisk.Disk):
    def __init__(self,
                 rid=None,
                 name=None,
                 vg=None,
                 **kwargs):
        resDisk.Disk.__init__(self,
                              rid=rid,
                              name=name,
                              type='disk.lv',
                              **kwargs)
        self.fullname = "%s/%s" % (vg, name)
        self.label = "lv %s" % self.fullname
        self.vg = vg

    def _info(self):
        data = [
          ["name", self.name],
          ["vg", self.vg],
        ]
        return data

    def has_it(self):
        attr = self.get_lv_attr()
        if attr is None:
            return False
        return True

    def is_up(self):
        """
        Returns True if the logical volume is present and activated
        """
        attr = self.get_lv_attr()
        if attr is None:
            return False
        if re.search('....a.', attr) is not None:
            return True
        return False

    def get_lv_attr(self):
        cmd = [rcEnv.syspaths.lvs, '-o', 'lv_attr', '--noheadings', '--separator=;', self.fullname]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return
        return out.strip()

    def activate_lv(self):
        cmd = ['lvchange', '-a', 'y', self.fullname]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def deactivate_lv(self):
        cmd = ['lvchange', '-a', 'n', self.fullname]
        ret, out, err = self.vcall(cmd, err_to_info=True)
        if ret != 0:
            raise ex.excError

    def do_start(self):
        if self.is_up():
            self.log.info("%s is already up" % self.label)
            return 0
        self.activate_lv()
        self.can_rollback = True

    def remove_dev_holders(self, devpath, tree):
        dev = tree.get_dev_by_devpath(devpath)
        holders_devpaths = set()
        holder_devs = dev.get_children_bottom_up()
        for holder_dev in holder_devs:
            holders_devpaths |= set(holder_dev.devpath)
        holders_devpaths -= set(dev.devpath)
        holders_handled_by_resources = self.svc.sub_devs() & holders_devpaths
        if len(holders_handled_by_resources) > 0:
            raise ex.excError("resource %s has holders handled by other resources: %s" % (self.rid, ", ".join(holders_handled_by_resources)))
        for holder_dev in holder_devs:
            holder_dev.remove(self)

    def remove_holders(self):
        tree = self.svc.node.devtree
        lvdev  = "/dev/mapper/%s-%s" % (
            self.vg.replace("-", "--"),
            self.name.replace("-", "--")
        )
        if "_rimage_" in lvdev or "_rmeta_" in lvdev or \
           "_mimage_" in lvdev or " _mlog_" in lvdev or \
           lvdev.endswith("_mlog"):
            return
        self.remove_dev_holders(lvdev, tree)

    def do_stop(self):
        if not self.is_up():
            self.log.info("%s is already down" % self.label)
            return
        self.remove_holders()
        udevadm_settle()
        self.deactivate_lv()

    def lv_devices(self):
        cmd = ["lvs", "-o", "devices", "--noheadings", self.fullname]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return set()
        devs = set()
        for dev in out.split():
            if "(" in dev:
                devs.add(dev[:dev.index("(")])
        return devs
        
    def sub_devs(self):
        if not self.has_it():
            return set()
        return self.lv_devices()

    def exposed_devs(self):
        if not self.has_it():
            return set()
        devs = set()
        lvp = "/dev/"+self.vg+"/"+self.name
        if os.path.exists(lvp):
            devs.add(lvp)
        return devs

   0707010005c9be000081b40000000000000000000000015ab2175400002e46000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/resDiskMdLinux.py  import re
import json
import os
import glob
import time
import rcExceptions as ex
import resDisk
import rcStatus
from rcGlobalEnv import rcEnv
from rcUtilitiesLinux import major, get_blockdev_sd_slaves, \
                             dev_to_paths
from rcUtilities import which, justcall, lazy, fcache

class Disk(resDisk.Disk):
    startup_timeout = 10

    def __init__(self,
                 rid=None,
                 uuid=None,
                 **kwargs):
        self.uuid = uuid
        self.mdadm = "/sbin/mdadm"
        resDisk.Disk.__init__(self,
                          rid=rid,
                          name=uuid,
                          type='disk.md',
                          **kwargs)
        if uuid:
            self.label = "md " + uuid
        else:
            self.label = "md"

    @lazy
    def mdadm_cf(self):
        if os.path.exists("/etc/mdadm"):
            return "/etc/mdadm/mdadm.conf"
        else:
            return "/etc/mdadm.conf"

    @lazy
    def is_shared(self):
        if self.shared is not None:
            return self.shared
        if len(self.svc.nodes|self.svc.drpnodes) < 2:
            self.log.debug("shared param defaults to 'false' due to single "
                           "node configuration")
            return False
        l = [option for option in self.svc.config.options(self.rid) if \
             option.startswith("uuid@")]
        if len(l) > 0:
            self.log.debug("shared param defaults to 'false' due to scoped "
                           "configuration")
            return False
        else:
            self.log.debug("shared param defaults to 'true' due to unscoped "
                           "configuration")
            return True

    def _info(self):
        data = [
          ["uuid", self.uuid],
        ]
        return data

    def md_config_file_name(self):
        return os.path.join(self.var_d, 'disks')

    def md_config_import(self):
        p = self.md_config_file_name()
        if not os.path.exists(p):
            return set()
        with open(p, "r") as ofile:
            return json.load(ofile)

    def md_config_export(self):
        devs = self.sub_devs()
        disk_ids = set()
        for dev in devs:
            treedev = self.svc.node.devtree.get_dev_by_devpath(dev)
            if not treedev:
                continue
            disk_ids.add(treedev.alias)
        with open(self.md_config_file_name(), "w") as ofile:
             json.dump(list(disk_ids), ofile)

    def postsync(self):
        pass

    def down_state_alerts(self):
        if not self.is_shared:
            return
        devnames = self.md_config_import()
        devnames = set([d for d in devnames if not d.startswith("md")])
        if len(devnames) == 0:
            return

        dt = self.svc.node.devtree
        aliases = set([d.alias for d in dt.dev.values()])
        not_found = devnames - aliases
        if len(not_found) > 0:
            self.status_log("md member missing: %s" % ", ".join(sorted(list(not_found))))

    def presync(self):
        if self.uuid is None:
            return
        if not self.is_shared:
            return
        s = self.svc.group_status(excluded_groups=set(["app", "sync", "hb"]))
        if self.svc.options.force or s['overall'].status == rcStatus.UP:
            self.md_config_export()

    def files_to_sync(self):
        if self.uuid is None:
            return
        if not self.is_shared:
            return []
        return [self.md_config_file_name()]

    def md_devpath(self):
        l = glob.glob("/dev/disk/by-id/md-uuid-"+self.uuid)
        if len(l) > 0:
            return l[0]
        out, err, ret = self.mdadm_scan()
        for line in out.splitlines():
            if self.uuid in line:
                devname = line.split()[1]
                if os.path.exists(devname):
                    return devname
        raise ex.excError("unable to find a devpath for md")

    def devpath(self):
        return "/dev/md/"+self.uuid

    def exposed_devs(self):
        if self.uuid == "" or self.uuid is None:
            return set()
        try:
            return set([os.path.realpath(self.md_devpath())])
        except:
            return set()

    def assemble(self):
        cmd = [self.mdadm, "--assemble", self.devpath(), "-u", self.uuid]
        ret, out, err = self.vcall(cmd, warn_to_info=True)
        if ret == 2:
            self.log.info("no changes were made to the array")
        elif ret != 0:
            raise ex.excError
        else:
            self.wait_for_fn(self.has_it, self.startup_timeout, 1, errmsg="waited too long for devpath creation")

    def manage_stop(self):
        cmd = [self.mdadm, "--stop", self.md_devpath()]
        ret, out, err = self.vcall(cmd, warn_to_info=True)
        if ret != 0:
            raise ex.excError

    def detail(self):
        try:
            devpath = self.md_devpath()
        except ex.excError as e:
            return "State : " + str(e)
        if not os.path.exists(devpath):
            return "State : devpath does not exist"
        cmd = [self.mdadm, "--detail", devpath]
        out, err, ret = justcall(cmd)
        if "cannot open /dev" in err:
            return "State : devpath does not exist"
        if ret != 0:
            if "does not appear to be active" in err:
                return "State : md does not appear to be active"
            raise ex.excError(err)
        return out

    def detail_status(self):
        buff = self.detail()
        for line in buff.split("\n"):
            line = line.strip()
            if line.startswith("State :"):
                return line.split(" : ")[-1]
        return "unknown"

    def has_it(self):
        if self.uuid == "" or self.uuid is None:
            return False
        return self.uuid in self.mdadm_scan_v()[0]

    def is_up(self):
        if not self.has_it():
            return False
        buff = self.detail_status()
        states = buff.split(", ")
        if len(states) > 1:
            self.status_log(buff, "warn")
        if "Not Started" in states or len(states) == 0:
            return False
        if "devpath does not exist" in states or \
           "unable to find a devpath for md" in states:
            return False
        return True

    def auto_assemble_disabled(self):
        if self.uuid == "" or self.uuid is None:
            return True
        if not os.path.exists(self.mdadm_cf):
            self.status_log("auto-assemble is not disabled")
            return False
        with open(self.mdadm_cf, "r") as ofile:
            for line in ofile.readlines():
                words = line.strip().split()
                if len(words) < 2:
                    continue
                if words[0] == "AUTO" and "-all" in words:
                    return True
                if words[0] == "ARRAY" and words[1] == "<ignore>" and \
                   "UUID="+self.uuid in words:
                    return True
        self.status_log("auto-assemble is not disabled")
        return False

    def auto_assemble_disable(self):
        if self.uuid == "" or self.uuid is None:
            return
        if self.auto_assemble_disabled():
            return
        self.log.info("disable auto-assemble in %s" % self.mdadm_cf)
        with open(self.mdadm_cf, "a+") as ofile:
            ofile.write("ARRAY <ignore> UUID=%s\n" % self.uuid)

    def _status(self, verbose=False):
        if self.uuid is None:
            return rcStatus.NA
        self.auto_assemble_disabled()
        s = resDisk.Disk._status(self, verbose=verbose)
        if s == rcStatus.DOWN:
             self.down_state_alerts()
        return s

    def do_start(self):
        if self.uuid is None:
            raise ex.excError("uuid is not set")
        self.auto_assemble_disable()
        if self.is_up():
            self.log.info("md %s is already assembled" % self.uuid)
            return 0
        self.can_rollback = True
        self.assemble()
        self._create_static_name()

    def do_stop(self):
        if self.uuid is None:
            self.log.warning("uuid is not set: skip")
            return
        self.auto_assemble_disable()
        if not self.is_up():
            self.log.info("md %s is already down" % self.uuid)
            return
        self.manage_stop()

    def _create_static_name(self):
        self.create_static_name(self.md_devpath())

    @fcache
    def sub_devs(self):
        if self.uuid == "" or self.uuid is None:
            # try to get the info from the config so pr co-resource can reserv
            # during provision
            try:
                return set([os.path.realpath(dev) for dev in self.conf_get("devs").split()])
            except ex.OptNotFound:
                return set()
        try:
            devpath = self.md_devpath()
        except ex.excError as e:
            return self.sub_devs_inactive()
        if os.path.exists(devpath):
            return self.sub_devs_active()
        else:
            return self.sub_devs_inactive()

    def mdadm_scan(self):
        cmd = [self.mdadm, "--detail", "--scan"]
        return justcall(cmd)

    def mdadm_scan_v(self):
        cmd = [self.mdadm, "-E", "--scan", "-v"]
        return justcall(cmd)

    def sub_devs_inactive(self):
        devs = set()
        out, err, ret = self.mdadm_scan_v()
        if ret != 0:
            return devs
        lines = out.split("\n")

        if len(lines) < 2:
            return set()
        inblock = False
        paths = set()
        for line in lines:
            if "UUID="+self.uuid in line:
                inblock = True
                continue
            if inblock and "devices=" in line:
                l = line.split("devices=")[-1].split(",")
                l = map(lambda x: os.path.realpath(x), l)
                for dev in l:
                    _paths = set(dev_to_paths(dev))
                    if set([dev]) != _paths:
                        paths |= _paths
                    devs.add(dev)
                break
        # discard paths from the list (mdadm shows both mpaths and paths)
        devs -= paths

        self.log.debug("found devs %s held by md %s" % (devs, self.uuid))
        return devs

    def sub_devs_active(self):
        devs = set()

        try:
            lines = self.detail().split("\n")
        except ex.excError as e:
            return set()

        if len(lines) < 2:
            return set()
        for line in lines[1:]:
            if "/dev/" not in line:
                continue
            devpath = line.split()[-1]
            devpath = os.path.realpath(devpath)
            devs.add(devpath)

        self.log.debug("found devs %s held by md %s" % (devs, self.uuid))
        return devs

    def sync_resync(self):
        faultydev = None
        buff = self.detail()
        removed = buff.count("removed")
        if removed == 0:
            self.log.info("skip: no removed device")
            return
        if "Raid Level : raid1" not in buff:
            self.log.info("skip: non-raid1 md")
            return
        if not self.is_up():
            self.log.info("skip: non-up md")
            return
        devpath = self.devpath()
        for line in buff.split("\n"):
            line = line.strip()
            if "faulty" in line:
                faultydev = line.split()[-1]
                cmd = [self.mdadm, "--re-add", devpath, faultydev]
                ret, out, err = self.vcall(cmd, warn_to_info=True)
                if ret != 0:
                    raise ex.excError("failed to re-add %s to %s"%(faultydev, devpath))
                added += 1
        if removed > added:
            self.log.error("no faulty device found to re-add to %s remaining "
                           "%d removed legs"% (devpath, removed - added))

  0707010005c9bf000081b40000000000000000000000015ab2175400002006000000660000000000000000000000000000003000000000root/usr/share/opensvc/lib/resDiskRadosLinux.py   import resDisk
import os
import rcStatus
import rcExceptions as ex
import json
from rcGlobalEnv import *
from rcUtilities import justcall

class Disk(resDisk.Disk):
    def __init__(self,
                 rid=None,
                 images=set(),
                 client_id=None,
                 keyring=None,
                 **kwargs):
        resDisk.Disk.__init__(self,
                          rid=rid,
                          type="disk.vg",
                          **kwargs)
        self.images = images
        self.keyring = keyring
        if not client_id.startswith("client."):
            client_id = "client."+client_id
        self.client_id = client_id
        self.label = self.fmt_label()
        self.modprobe_done = False

    def validate_image_fmt(self):
        l = []
        for image in self.images:
            if "/" not in image:
                l.append(image)
        if len(l):
            raise ex.excError("wrong format (expected pool/image): "+", ".join(l))

    def fmt_label(self):
        s = "rados images: "
        s += ", ".join(self.images)
        return s

    def modprobe(self):
        if self.modprobe_done:
            return
        cmd = [rcEnv.syspaths.lsmod]
        ret, out, err = self.call(cmd)
        if ret != 0:
            raise ex.excError("lsmod failed")
        if "rbd" in out.split():
            # no need to load (already loaded or compiled-in)
            return
        cmd = ["modprobe", "rbd"]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError("failed to load rbd device driver")
        self.modprobe_done = True

    def showmapped(self, refresh=False):
        if not refresh and hasattr(self, "mapped_data"):
            return self.mapped_data
        self.modprobe()
        cmd = ["rbd", "showmapped", "--format", "json"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError("rbd showmapped failed: "+err)
        try:
            _data = json.loads(out)
        except Exception as e:
            raise ex.excError(str(e))
        data = {}
        for id, img_data in _data.items():
            data[img_data["pool"]+"/"+img_data["name"]] = img_data
        self.mapped_data = data
        return data

    def rbd_rcmd(self):
        l = ["rbd", "-n", self.client_id]
        if self.keyring:
            l += ["--keyring", self.keyring]
        return l

    def exists(self, image):
        cmd = self.rbd_rcmd()+["info", image]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return False
        return True

    def has_it(self, image):
        mapped = self.showmapped()
        if image in mapped:
            return True
        return False

    def up_count(self):
        mapped = self.showmapped()
        l = []
        for image in self.images:
            if image in mapped:
                l.append(image)
        return l

    def _status(self, verbose=False):
        try:
            self.validate_image_fmt()
        except Exception as e:
            self.status_log(str(e))
            return rcStatus.WARN
        l = self.up_count()
        n = len(l)
        unmapped = sorted(list(set(self.images) - set(l)))
        if n == len(self.images):
            return rcStatus.UP
        elif n == 0:
            return rcStatus.DOWN
        else:
            self.status_log("unmapped: "+", ".join(unmapped))
            return rcStatus.DOWN

    def devname(self, image):
        return os.path.join(os.sep, "dev", "rbd", image)

    def do_start_one(self, image):
        mapped = self.showmapped()
        if image in mapped:
            self.log.info(image+" is already mapped")
            return
        cmd = self.rbd_rcmd()+["map", image]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError("failed to map %s"%self.devname(image))

    def do_start(self):
        self.validate_image_fmt()
        for image in self.images:
            self.do_start_one(image)
            self.can_rollback = True
        self.showmapped(refresh=True)

    def do_stop_one(self, image):
        mapped = self.showmapped()
        if image not in mapped:
            self.log.info(image+" is already unmapped")
            return
        cmd = ["rbd", "unmap", self.devname(image)]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError("failed to unmap %s"%self.devname(image))


    def do_stop(self):
        self.validate_image_fmt()
        for image in self.images:
            self.do_stop_one(image)
        self.showmapped(refresh=True)

    def exposed_disks(self):
        l = set()
        for image in self.images:
            s = ".".join(("rbd", image.replace("/", ".")))
            l.add(s)
        return l

    def exposed_devs(self):
        l = set()
        for image in self.images:
            s = self.devname(image)
            s = os.path.realpath(s)
            l.add(s)
        return l

class DiskLock(Disk):
    def __init__(self,
                 rid=None,
                 type="disk.lock",
                 images=set(),
                 client_id=None,
                 keyring=None,
                 lock=None,
                 lock_shared_tag=None,
                 optional=False,
                 disabled=False,
                 tags=set(),
                 standby=False,
                 monitor=False,
                 restart=0,
                 subset=None):

        self.lock = lock
        self.lock_shared_tag = lock_shared_tag

        Disk.__init__(self,
                    rid=rid,
                    type=type,
                    images=images,
                    client_id=client_id,
                    keyring=keyring,
                    optional=optional,
                    disabled=disabled,
                    tags=tags,
                    standby=standby,
                    monitor=monitor,
                    restart=restart,
                    subset=subset)

        self.label = self.fmt_label()
        self.unlocked = []


    def fmt_label(self):
        return str(self.lock) + " lock on " + Disk.fmt_label(self)

    def locklist(self, image):
        cmd = self.rbd_rcmd()+["lock", "list", image, "--format", "json"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError("rbd lock list failed")
        data = {}
        try:
            data = json.loads(out)
        except Exception as e:
            raise ex.excError(str(e))
        return data


    def has_lock(self, image):
        data = self.locklist(image)
        if rcEnv.nodename in data:
            return True
        self.unlocked.append(image)
        return False

    def up_count(self):
        n = 0
        for image in self.images:
            if self.has_lock(image):
                n += 1
        return n

    def _status(self, verbose=False):
        n = self.up_count()
        if n == len(self.images):
            return rcStatus.UP
        elif n == 0:
            return rcStatus.DOWN
        else:
            self.status_log("unlocked: "+", ".join(self.unlocked))
            return rcStatus.DOWN

    def do_stop_one(self, image):
        data = self.locklist(image)
        if rcEnv.nodename not in data:
            self.log.info(image+" is already unlocked")
            return
        i = 0
        while len(data) > 0 or i>20:
            i += 1
            cmd = self.rbd_rcmd()+["lock", "remove", image, rcEnv.nodename, data[rcEnv.nodename]["locker"]]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                raise ex.excError("failed to unlock %s"%self.devname(image))
            data = self.locklist(image)

    def do_start_one(self, image):
        data = self.locklist(image)
        if rcEnv.nodename in data:
            self.log.info(image+" is already locked")
            return
        cmd = self.rbd_rcmd()+["lock", "add", image, rcEnv.nodename]
        if self.lock_shared_tag:
            cmd += ["--shared", self.lock_shared_tag]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError("failed to lock %s"%self.devname(image))


  0707010005c9c0000081b40000000000000000000000015ab217540000319a000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/resDiskRaw.py  import resDisk
import os
import rcStatus
import re
import pwd
import grp
import stat
import sys
import glob
import rcExceptions as ex
from rcUtilities import which, is_string, lazy

class Disk(resDisk.Disk):
    def __init__(self,
                 rid=None,
                 devs=set(),
                 user=None,
                 group=None,
                 perm=None,
                 create_char_devices=False,
                 **kwargs):
        resDisk.Disk.__init__(self,
                          rid=rid,
                          name="raw",
                          type='disk.raw',
                          **kwargs)

        self.label = "raw"
        self.user = user
        self.group = group
        self.perm = perm
        self.create_char_devices = create_char_devices
        self.original_devs = devs

        self.devs = set()
        self.devs_not_found = set()
        self.dst_devs_not_found = set()
        self.major_minor_errs = set()
        self.devs_map = {}

    def reload_config(self):
        """
        The dev parameter can use exposed_devs reference that were not
        resolvable at build time, as the ressource exposing can be down.
        """
        self.svc.ref_cache = {}
        self.clear_caches()
        self.devs = set()
        self.original_devs = self.conf_get('devs')
        try:
            zone = self.conf_get('zone')
        except ex.OptNotFound as exc:
            zone = exc.default
        if zone is not None:
            self.original_devs = set([dev.replace(":", ":<%s>" % zone) for dev in self.original_devs])

    def clear_caches(self):
        pass

    def verify_dev(self, path):
        # os specific plug
        return True

    def _info(self):
        self.validate_devs()
        data = []
        if self.create_char_devices:
            data += [["create_char_devices", str(self.create_char_devices)]]
        if self.user:
            data += [["user", str(self.user)]]
        if self.group:
            data += [["group", str(self.group)]]
        if self.perm:
            data += [["perm", str(self.perm)]]
        for dev in self.devs:
            if dev in self.devs_map:
                data += [["dev", dev+":"+self.devs_map[dev]]]
            else:
                data += [["dev", dev]]
        return data

    def subst_container_root(self, path):
        m = re.match("<(\w+)>", path)
        if m is None:
            return path
        container_name = m.group(1)
        for r in self.svc.get_resources("container"):
            if hasattr(r, "name") and r.name == container_name:
                if hasattr(r, "get_zonepath"):
                    # zone
                    container_root = r.get_zonepath()
                elif hasattr(r, "get_rootfs"):
                    # lxc
                    container_root = r.get_rootfs()
                else:
                    return path
                path = re.sub("<\w+>", container_root, path)
                break
        return path

    def validate_devs(self):
        self.devs = set()
        self.devs_not_found = set()
        self.dst_devs_not_found = set()
        for dev in self.original_devs:
            if ":" in dev:
                try:
                    src, dst = dev.split(":")
                except:
                    continue
                if not os.path.exists(src) or not self.verify_dev(src):
                    self.devs_not_found.add(src)
                    continue
                dst = self.subst_container_root(dst)
                if not os.path.exists(dst):
                    self.dst_devs_not_found.add(dst)
                self.devs_map[src] = dst
                self.devs.add(src)
                continue
            l = set(glob.glob(dev))
            if len(l) > 0:
                for _dev in l:
                    if not self.verify_dev(_dev):
                        continue
                    self.devs.add(_dev)
            else:
                self.devs_not_found.add(dev)

    def on_add(self):
        try:
            n = self.rid.split('#')[1]
        except:
            n = "0"
        self.name = self.svc.svcname+".raw"+n
        self.label = self.name

    @lazy
    def uid(self):
        uid = self.user
        if is_string(uid):
            try:
                info=pwd.getpwnam(uid)
                uid = info[2]
            except:
                pass
        return uid

    @lazy
    def gid(self):
        gid = self.group
        if is_string(gid):
            try:
                info=grp.getgrnam(gid)
                gid = info[2]
            except:
                pass
        return gid

    def check_uid(self, rdev, verbose=False):
        if not os.path.exists(rdev):
            return True
        if self.user is None:
            return True
        if self.uid is None:
            if verbose:
                self.status_log('user %s uid not found'%str(self.user))
            return False
        uid = os.stat(rdev).st_uid
        if uid != self.uid:
            if verbose:
                self.status_log('%s uid should be %s but is %d'%(rdev, str(self.uid), uid))
            return False
        return True

    def check_gid(self, rdev, verbose=False):
        if not os.path.exists(rdev):
            return True
        if self.group is None:
            return True
        if self.gid is None:
            if verbose:
                self.status_log('group %s gid not found'%str(self.group))
            return False
        gid = os.stat(rdev).st_gid
        if gid != self.gid:
            if verbose:
                self.status_log('%s gid should be %s but is %d'%(rdev, str(self.gid), gid))
            return False
        return True

    def check_perm(self, rdev, verbose=False):
        if not os.path.exists(rdev):
            return True
        try:
            perm = oct(stat.S_IMODE(os.stat(rdev).st_mode))
        except:
            self.log.error('%s can not stat file'%rdev)
            return False
        perm = str(perm).lstrip("0o").lstrip("0")
        if perm != str(self.perm):
            if verbose:
                self.status_log('%s perm should be %s but is %s'%(rdev, str(self.perm), perm))
            return False
        return True

    def check_dst(self, src, dst):
        if dst in self.major_minor_errs:
            self.major_minor_errs.remove(dst)
        if src is None or dst is None:
            return False
        if not os.path.exists(src) or not os.path.exists(dst):
            return False
        src_st = os.stat(src)
        dst_st = os.stat(dst)
        r = True
        if os.major(src_st.st_rdev) != os.major(dst_st.st_rdev) or \
           os.minor(src_st.st_rdev) != os.minor(dst_st.st_rdev):
            self.major_minor_errs.add(dst)
            r &= False
        return r

    def fix_ownership(self, path):
        self.fix_ownership_user(path)
        self.fix_ownership_group(path)

    def fix_ownership_user(self, path):
        if self.user is None:
            return
        if self.uid is None:
            raise ex.excError("user %s does not exist" % str(self.user))
        if not self.check_uid(path):
            self.vcall(['chown', str(self.uid), path])
        else:
            self.log.info("%s has correct user ownership (%s)"% (path, str(self.uid)))

    def fix_ownership_group(self, path):
        if self.group is None:
            return
        if self.gid is None:
            raise ex.excError("group %s does not exist" % str(self.group))
        if self.gid and not self.check_gid(path):
            self.vcall(['chgrp', str(self.gid), path])
        else:
            self.log.info("%s has correct group ownership (%s)"% (path, str(self.gid)))

    def fix_perms(self, path):
        if self.uid is None:
            return
        if not self.check_perm(path):
            self.vcall(['chmod', self.perm, path])
        else:
            self.log.info("%s has correct permissions (%s)"% (path, str(self.perm)))

    def mangle_devs_map(self):
        pass

    def has_it_char_devices(self):
        return True

    def has_it_devs_map(self):
        r = True
        if len(self.dst_devs_not_found) == len(self.devs_map):
            # all dst unlinked: report no error => down state
            r &= False
        elif len(self.dst_devs_not_found) > 0:
            self.status_log("%s dst devs not found"%', '.join(self.dst_devs_not_found))
            r &= False
        for src, dst in self.devs_map.items():
            r &= self.has_it_dev_map(src, dst)
        return r

    def has_it_dev_map(self, src, dst):
        r = True
        if not self.check_dst(src, dst):
            r &= False
        if not self.check_uid(dst, verbose=True):
            r &= False
        elif not self.check_gid(dst, verbose=True):
            r &= False
        elif not self.check_perm(dst, verbose=True):
            r &= False
        return r

    def has_it(self):
        """Returns True if all raw devices are present and correctly
           named
        """
        r = True
        if self.create_char_devices:
            r &= self.has_it_char_devices()
        if len(self.devs_map) > 0:
            r &= self.has_it_devs_map()
        if len(self.devs_not_found) > 0:
            status = self.svc.group_status(excluded_groups=set([
                "sync",
                "app",
                "disk",
                "hb"
            ]))
            msg = "%s not found"%', '.join(self.devs_not_found)
            if str(status["avail"]) not in ("up", "n/a"):
                self.status_log(msg, "info")
            else:
                self.status_log(msg, "warn")
            r &= False
        if len(self.major_minor_errs) > 0:
            self.status_log("%s have major:minor diff with their src" % ', '.join(sorted(list(self.major_minor_errs))))
            r &= False
        return r

    def is_up(self):
        """Returns True if the volume group is present and activated
        """
        return self.has_it()

    def _status(self, verbose=False):
        self.validate_devs()
        self.mangle_devs_map()
        r = self.is_up()
        if not self.create_char_devices and len(self.devs_map) == 0:
            return rcStatus.NA
        if r:
            return rcStatus.UP
        else:
            return rcStatus.DOWN

    def do_start(self):
        self.reload_config()
        self.validate_devs()
        self.can_rollback = True
        self.do_start_char_devices()
        self.mangle_devs_map()
        self.do_start_blocks()

    def do_start_block(self, src, dst):
        if src is not None:
            if not os.path.exists(src):
                raise ex.excError("src file %s does not exist" % src)
            d = os.path.dirname(dst)
            if not os.path.exists(d):
                self.log.info("create dir %s" % d)
                os.makedirs(d)
            if not os.path.exists(dst) or not self.check_dst(src, dst):
                self.do_create_dst(src, dst)
        self.fix_ownership(dst)
        self.fix_perms(dst)

    def do_create_dst(self, src, dst):
        if os.path.exists(dst):
            self.log.info("remove existing dst %s", dst)
            os.unlink(dst)
        src_st = os.stat(src)
        if stat.S_ISBLK(src_st.st_mode):
            t = "b"
        elif stat.S_ISCHR(src_st.st_mode):
            t = "c"
        else:
            raise ex.excError("%s is not a block nor a char device" % src)
        major = os.major(src_st.st_rdev)
        minor = os.minor(src_st.st_rdev)
        cmd = ["mknod", dst, t, str(major), str(minor)]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def do_start_blocks(self):
        if which("mknod") is None:
            raise ex.excError("mknod not found")
        for src, dst in self.devs_map.items():
            self.do_start_block(src, dst)

    def do_start_char_devices(self):
        pass

    def do_stop_char_devices(self):
        pass

    def do_stop(self):
        self.validate_devs()
        self.mangle_devs_map()
        self.do_stop_blocks()
        self.do_stop_char_devices()

    def do_stop_blocks(self):
        for src, dst in self.devs_map.items():
            self.do_stop_block(src, dst)

    def do_stop_block(self, src, dst):
        if src is None:
            # never unlink unmapped devs
            return
        if os.path.exists(dst):
            self.log.info("unlink %s" % dst)
            try:
                os.unlink(dst)
            except Exception as e:
                raise ex.excError(str(e))
        else:
            self.log.info("%s already unlinked" % dst)

    def sub_devs(self):
        return self.devs

  0707010005c9c1000081b40000000000000000000000015ab2175400000358000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/resDiskRawHP-UX.py import resDiskRaw
import os
import rcStatus
import re
from rcUtilities import justcall

class Disk(resDiskRaw.Disk):
    def __init__(self,
                 rid=None,
                 devs=set(),
                 user=None,
                 group=None,
                 perm=None,
                 create_char_devices=False,
                 **kwargs):

        resDiskRaw.Disk.__init__(self,
                             rid=rid,
                             devs=devs,
                             user=user,
                             group=group,
                             perm=perm,
                             create_char_devices=False,
                             **kwargs)

    def verify_dev(self, dev):
        cmd = ["diskinfo", dev]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return False
        return True
0707010005c9c2000081b40000000000000000000000015ab2175400002335000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/resDiskRawLinux.py import resDiskRaw
import os
import rcStatus
import re
import rcExceptions as ex
from rcGlobalEnv import *
from rcUtilities import justcall, cache, lazy
from rcUtilitiesLinux import devs_to_disks

class Disk(resDiskRaw.Disk):
    def __init__(self,
                 rid=None,
                 devs=set(),
                 create_char_devices=True,
                 user=None,
                 group=None,
                 perm=None,
                 **kwargs):

        resDiskRaw.Disk.__init__(self,
                             rid=rid,
                             devs=devs,
                             user=user,
                             group=group,
                             perm=perm,
                             create_char_devices=create_char_devices,
                             **kwargs)
        self.min_raw = 1
        self.sys_devs = {}

    @lazy
    def devs_t(self):
        devs_t = {}
        for dev in self.devs:
            d = os.stat(dev)
            devs_t[dev] = (os.major(d.st_rdev), os.minor(d.st_rdev))
        return devs_t

    @cache("raw.rdevs_t")
    def _rdevs_t(self):
        rdevs_t = {}
        for dirpath, dirnames, filenames in os.walk('/dev/raw'):
            for filename in filenames:
                f = os.path.join(dirpath, filename)
                d = os.stat(f)
                key = "%d:%d" % (os.major(d.st_rdev), os.minor(d.st_rdev))
                rdevs_t[key] = filename
        return rdevs_t

    @lazy
    def rdevs_t(self):
        return self._rdevs_t()

    @cache("raw.modprobe")
    def modprobe(self):
        """
        Load the raw driver if necessary.
        Cached to execute only once per run. The result is of no interest.
        """
        cmd = ["raw", "-qa"]
        err, ret, out = justcall(cmd)
        if ret == 0:
            # no need to load (already loaded or compiled-in)
            return
        cmd = [rcEnv.syspaths.lsmod]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError
        if "raw" in out.split():
            return
        cmd = ["modprobe", "raw"]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            self.log.error("failed to load raw device driver")
            raise ex.excError

    @cache("raw.list")
    def get_raws(self):
        self.modprobe()
        raws = {}
        sysfs_raw_path = os.path.join(os.sep, 'sys', 'class', 'raw')
        for dirpath, dirnames, filenames in os.walk(sysfs_raw_path):
            for dirname in dirnames:
                if dirname == 'rawctl':
                    continue
                dev = os.path.join(dirpath, dirname, 'dev')
                if not os.path.exists(dev):
                    continue
                with open(dev, 'r') as f:
                    buff = f.read()
                try:
                    major, minor = buff.strip().split(':')
                except:
                    continue
                raws[dirname] = {'rdev': (int(major), int(minor))}

        cmd = ['raw', '-qa']
        out, err, ret = justcall(cmd)
        if ret != 0:
            self.log.error('failed to fetch raw device bindings')
            raise ex.excError
        for line in out.split('\n'):
            l = line.split()
            if len(l) != 7:
                continue
            raw = l[0].strip(':').replace('/dev/raw/','')
            major = int(l[4].strip(','))
            minor = int(l[6])
            if raw in raws:
                raws[raw]['bdev'] = (major, minor)
        return raws

    @lazy
    def raws(self):
        raws = self.get_raws()
        for dev, dev_t in self.devs_t.items():
            for raw, d in raws.items():
                if list(dev_t) == list(d['bdev']):
                    raws[raw]['devname'] = dev

        for raw, d in raws.items():
            key = "%d:%d" % (d['rdev'][0], d['rdev'][1])
            if key in self.rdevs_t:
                raws[raw]['rdevname'] = self.rdevs_t[key]
        return raws

    def find_next_raw(self):
        allocated = set(map(lambda x: x['rdev'][1], self.raws.values()))
        candidates = set(range(1, 255))
        candidates -= allocated
        if len(candidates) == 0:
            self.log.error("no more raw device can be allocated")
            raise ex.excError
        return 'raw%d'%sorted(list(candidates))[0]

    def find_raw(self, dev):
        for raw, d in self.raws.items():
            if 'devname' in d and dev == d['devname']:
                return raw
        return None

    def lock(self, timeout=30, delay=1):
        import lock
        lockfile = os.path.join(rcEnv.paths.pathlock, 'startvgraw')
        lockfd = None
        try:
            lockfd = lock.lock(timeout=timeout, delay=delay, lockfile=lockfile)
        except lock.LockTimeout:
            self.log.error("timed out waiting for lock")
            raise ex.excError
        except lock.LockNoLockFile:
            self.log.error("lock_nowait: set the 'lockfile' param")
            raise ex.excError
        except lock.LockCreateError:
            self.log.error("can not create lock file %s"%lockfile)
            raise ex.excError
        except lock.LockAcquire as e:
            self.log.warn("another action is currently running (pid=%s)"%e.pid)
            raise ex.excError
        except ex.excSignal:
            self.log.error("interrupted by signal")
            raise ex.excError
        except:
            self.save_exc()
            raise ex.excError("unexpected locking error")
        self.lockfd = lockfd

    def unlock(self):
        import lock
        lock.unlock(self.lockfd)

    def has_it_char_devices(self):
        r = True
        l = []
        for dev in self.devs:
            raw = self.find_raw(dev)
            if raw is None:
                l.append(dev)
                r &= False
        if len(l) > 0 and len(l) < len(self.devs):
            self.status_log("%s not mapped to a raw device"% ", ".join(l))
        return r

    def mangle_devs_map(self):
        if not self.create_char_devices:
            return
        for dev in self.devs:
            raw = self.find_raw(dev)
            if dev in self.devs_map:
                if raw is not None:
                    self.devs_map["/dev/raw/"+raw] = self.devs_map[dev]
                del(self.devs_map[dev])

    def do_start_char_devices(self):
        if not self.create_char_devices:
            return
        self.lock()
        for dev in self.devs:
            raw = self.find_raw(dev)
            if raw is not None:
                self.log.info("%s is already mapped to a raw device"%dev)
            else:
                raw = self.find_next_raw()
                cmd = ['raw', "/dev/raw/"+raw, dev]
                ret, out, err = self.vcall(cmd)
                if ret != 0:
                    self.unlock()
                    raise ex.excError
                s = os.stat("/dev/raw/"+raw)
                self.raws[raw] = {
                  'rdev': (os.major(s.st_rdev), os.minor(s.st_rdev)),
                  'devname': dev,
                  'bdev': self.devs_t[dev]
                }

        self.clear_caches()
        self.unlock()

    def clear_caches(self):
        self.clear_cache("raw.list")
        self.clear_cache("raw.rdevs_t")
        self.unset_lazy("devs_t")
        self.unset_lazy("rdevs_t")
        self.unset_lazy("raws")

    def do_stop_char_devices(self):
        if not self.create_char_devices:
            return
        for dev in self.devs:
            raw = self.find_raw(dev)
            if raw is not None:
                cmd = ['raw', '/dev/raw/raw%d'%self.raws[raw]['rdev'][1], '0', '0']
                ret, out, err = self.vcall(cmd)
                if ret != 0:
                    raise ex.excError
                del(self.raws[raw])
        self.clear_caches()

    def load_sys_devs(self):
        import glob
        if self.sys_devs != {}:
            return
        for e in glob.glob('/sys/block/*/dev'):
            with open(e, 'r') as f:
                dev = e.replace('/sys/block/', '').replace('/dev', '')
                dev_t = f.read().strip()
                self.sys_devs[dev_t] = '/dev/'+dev

    def sub_disks(self):
        sys_devs = self.sub_devs()
        return devs_to_disks(self, sys_devs)

    def sub_devs(self):
        """ Admins can set arbitrary named devices, for example /dev/oracle/DGREDO_MYSID.
            Resolve those names into well known systems device names, so that they can be
            found in the DevTree
        """
        self.validate_devs()
        if not self.create_char_devices:
            return self.devs
        sys_devs = set()
        self.load_sys_devs()
        for dev in self.devs:
            if dev not in self.devs_t:
                continue
            dev_t = ':'.join(map(str, self.devs_t[dev]))
            if dev_t not in self.sys_devs:
                continue
            sys_dev = self.sys_devs[dev_t]
            sys_devs.add(sys_dev)
        return sys_devs

   0707010005c9c3000081b40000000000000000000000015ab21754000003ab000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/resDiskRawSunOS.py from __future__ import print_function

import os
import re

import rcStatus
import resDiskRaw

class Disk(resDiskRaw.Disk):
    def sub_disks(self):
        devs = self.sub_devs()
        l = set()
        for dev in devs:
            if re.match("^/dev/rdsk/c[0-9]*", dev) is None:
                continue
            if not os.path.exists(dev):
                continue

            if re.match('^.*s[0-9]*$', dev) is None:
                dev += "s2"
            else:
                regex = re.compile('s[0-9]*$', re.UNICODE)
                dev = regex.sub('s2', dev)

            l.add(dev)
        return l

    def sub_devs(self):
        self.validate_devs()
        l = set()
        for dev in self.devs:
            if not os.path.exists(dev):
                continue
            if os.path.islink(dev) and not dev.startswith("/devices"):
                dev = os.path.realpath(dev)
            l.add(dev)
        return l

 0707010005c9c4000081b40000000000000000000000015ab217540000072b000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/resDiskVdisk.py    """ Module providing device path remapping for libvirt VMs
"""

import resources as Res
import rcStatus
import rcExceptions as ex
from rcGlobalEnv import rcEnv

class Disk(Res.Resource):

    def __init__(self,
                 rid=None,
                 name=None,
                 devpath={},
                 **kwargs):
        Res.Resource.__init__(self,
                              rid,
                              "disk.vdisk",
                              **kwargs)
        self.label = "vdisk "+name
        self.name = name
        self.devpath = devpath

    def __str__(self):
        return "%s name=%s" % (Res.Resource.__str__(self), self.name)

    def sub_devs(self):
        return self.devpath.keys()

    def remap(self):
        path = self.devpath[rcEnv.nodename]
        paths = set(self.devpath.values()) - set(self.devpath[rcEnv.nodename])
        from xml.etree.ElementTree import ElementTree, SubElement
        tree = ElementTree()
        try:
            tree.parse(self.svc.resources_by_id['container'].cf)
        except:
            self.log.error("failed to parse %s"%self.svc.resources_by_id['container'].cf)
            raise ex.excError
        for dev in tree.getiterator('disk'):
            s = dev.find('source')
            if s is None:
                 continue
            il = s.items()
            if len(il) != 1:
                 continue
            attr, devp = il[0]
            if devp in paths:
                self.log.info("remapping device path: %s -> %s"%(devp,path))
                s.set('dev', path)
                #SubElement(dev, "source", {'dev': path})
                tree.write(self.svc.resources_by_id['container'].cf)

    def stop(self):
        pass

    def start(self):
        self.remap()

    def _status(self, verbose=False):
        return rcStatus.NA

 0707010005c9c5000081b40000000000000000000000015ab21754000020b9000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/resDiskVgAIX.py    import rcExceptions as ex
import resDisk
import json
import os
import logging
import shutil

from rcGlobalEnv import rcEnv
from subprocess import *


# ajouter un dump regulier de la config des vg (pour ne pas manquer les extensions de vol)

class Disk(resDisk.Disk):
    def __init__(self,
                 rid=None,
                 name=None,
                 dsf=True,
                 **kwargs):
        resDisk.Disk.__init__(self,
                          rid=rid,
                          name=name,
                          type='disk.vg',
                          **kwargs)
        self.label = "vg "+name
        self.dsf = dsf

    def has_it(self):
        """ returns True if the volume is present
        """
        if self.is_active():
            return True
        if self.is_imported():
            return True
        return False

    def is_active(self):
        cmd = [ 'lsvg', self.name ]
        process = Popen(cmd, stdout=PIPE, stderr=PIPE, close_fds=True)
        buff = process.communicate()
        if not "active" in buff[0]:
            return False
        return True

    def is_imported(self):
        cmd = ['lsvg']
        process = Popen(cmd, stdout=PIPE, stderr=PIPE, close_fds=True)
        buff = process.communicate()
        for vg in buff[0].split('\n'):
            if vg == self.name:
                return True
        return False

    def is_up(self):
        """Returns True if the volume group is present and activated
        """
        if not self.is_imported():
            return False
        if not self.is_active():
            return False
        return True

    def pvid2hdisk(self,mypvid):
        cmd = ['lspv']
        process = Popen(cmd, stdout=PIPE, stderr=PIPE, close_fds=True)
        buff = process.communicate()
        hdisk = "notfound"
        for line in buff[0].split('\n'):
            if mypvid in line:
                elem = line.split()
                #print("<%s> {%s}"%(line, elem[0]))
                return elem[0]    # first hdisk name matching requested pvid

    def dumped_pvids(self, p):
        if not os.path.exists(p):
            return []
        with open(p) as f:
            s = f.read()
        try:
            data = json.loads(s)
        except:
            return []
        l = []
        for line in data:
            pvid = line.get('pvid')
            if pvid is not None:
                l.append(pvid)
        return l

    def dump_changed(self):
        pvids1 = self.dumped_pvids(self.vgfile_name())
        pvids2 = self.dumped_pvids(self.vgimportedfile_name())
        if set(pvids1) == set(pvids2):
            return False
        return True

    def do_import(self):
        if not os.path.exists(self.vgfile_name()):
            raise ex.excError("%s should exist" % self.vgfile_name())
        if not self.dump_changed() and self.is_imported():
            self.log.info("%s is already imported" % self.name)
            return
        if self.dump_changed() and self.is_imported():
            if self.is_active():
                self.log.warning("%s is active. can't reimport." % self.name)
                return
            self.do_export()
        with open(self.vgfile_name()) as f:
            s = f.read()
        try:
            data = json.loads(s)
        except:
            raise ex.excError("%s is misformatted" % self.vgfile_name())
        self.pvids = {}
        missing = []
        for l in data:
            pvid = l.get('pvid')
            if pvid is None:
                continue
            hdisk = self.pvid2hdisk(pvid)
            self.pvids[pvid] = hdisk
            if hdisk == "notfound":
                missing.append(pvid)

        # check for missing devices
        if len(missing) > 1:
            raise ex.excError("Missing hdisks for pvids %s to be able to import vg" % ','.join(missing))
        elif len(missing) == 1:
            raise ex.excError("Missing hdisk for pvid %s to be able to import vg" % ','.join(missing))

        myhdisks = self.pvids.values()
        cmd = ['importvg', '-n', '-y', self.name, myhdisks[0]]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError
        shutil.copy2(self.vgfile_name(), self.vgimportedfile_name())

    def do_export(self):
        if not self.is_imported():
            self.log.info("%s is already exported" % self.name)
            return
        cmd = ['exportvg', self.name]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def do_activate(self):
        if self.is_active():
            self.log.info("%s is already available" % self.name)
            return
        cmd = ['varyonvg', self.name]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def do_deactivate(self):
        if not self.is_active():
            self.log.info("%s is already unavailable" % self.name)
            return
        cmd = ['varyoffvg', self.name]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def do_start(self):
        self.do_import()
        self.do_activate()
        self.do_dumpcfg()
        self.can_rollback = True

    def do_stop(self):
        self.do_dumpcfg()
        self.do_deactivate()

    def vgfile_name(self):
        return os.path.join(self.var_d, self.name + '.vginfo')

    def vgimportedfile_name(self):
        return os.path.join(self.var_d, self.name + '.vginfo.imported')

    def files_to_sync(self):
        return [self.vgfile_name()]

    def do_dumpcfg(self):
        cmd = ['lspv']
        p = Popen(cmd, stdout=PIPE, stderr=PIPE, close_fds=True)
        out, err = p.communicate()
        if p.returncode != 0:
            return
        data = []
        for line in out.split('\n'):
            l = line.split()
            n = len(l)
            h = {}
            for i, key in enumerate(['hdisk', 'pvid', 'vg', 'state']):
                if i >= n -1:
                    break
                h[key] = l[i]
            vg = h.get('vg')
            if vg is not None and vg == self.name:
                data.append(h)
        if len(data) == 0:
            # don't overwrite existing dump file with an empty dataset
            return
        s = json.dumps(data)
        with open(self.vgfile_name(), 'w') as f:
            f.write(s)

        """
        root@host:/$ lspv
        hdisk0          00078e0b282e417a                    rootvg          active
        hdisk1          none                                None
        hdisk2          00078e0bb1618c92                    tstvg           active
        hdisk3          00078e0bb161b59e                    tstvg           active
        hdisk4          none                                None
        hdisk5          none                                None

        =>

        [{'hdisk': 'hdisk0', 'pvid': '00078e0b282e417a', 'vg': 'rootvg', 'state': 'active'},
         {'hdisk': 'hdisk1', 'pvid': 'none', 'vg': 'None'},
         {'hdisk': 'hdisk2', 'pvid': '00078e0bb1618c92', 'vg': 'testvg', 'state': 'active'},
         {'hdisk': 'hdisk3', 'pvid': '00078e0bb161b59e', 'vg': 'testvg', 'state': 'active'},
         {'hdisk': 'hdisk4', 'pvid': 'none', 'vg': 'None'},
         {'hdisk': 'hdisk5', 'pvid': 'none', 'vg': 'None'}]
        """

    def sub_devs(self):
        if self.is_active():
            return self.sub_devs_active()
        return self.sub_devs_inactive()

    def sub_devs_active(self):
        devs = set()
        cmd = ['lsvg', '-p', self.name]
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            raise ex.excError

        for e in out.split('\n'):
            x = e.split()
            if len(x) != 5:
                continue
            devs |= set([x[0]])

        return devs

    def sub_devs_inactive(self):
        devs = set()
        if not os.path.exists(self.vgfile_name()):
            return devs
        with open(self.vgfile_name()) as f:
            s = f.read()
        try:
            data = json.loads(s)
        except:
            return devs
        for l in data:
            pvid = l.get('pvid')
            if pvid is None:
                continue
            hdisk = self.pvid2hdisk(pvid)
            if hdisk == "notfound":
                continue
            devs.add(hdisk)
        return devs

   0707010005c9c6000081b40000000000000000000000015ab2175400000b54000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/resDiskVgAdvfs.py  from __future__ import print_function

import os
import json
import re

import rcExceptions as ex
import resDisk
from rcUtilities import justcall
from rcGlobalEnv import rcEnv

class Disk(resDisk.Disk):
    def __init__(self,
                 rid=None,
                 name=None,
                 **kwargs):
        resDisk.Disk.__init__(self,
                          rid=rid,
                          name=name,
                          type='disk.vg',
                          **kwargs)
        self.label = 'fdmn ' + name

    def sub_devs_name(self):
        return os.path.join(self.var_d, 'sub_devs')

    def files_to_sync(self):
        return [self.sub_devs_name()]

    def presync(self):
        """ this one is exported as a service command line arg
        """
        dl = self._sub_devs()
        with open(self.sub_devs_name(), 'w') as f:
            json.dump(list(dl), f)

    def has_it(self):
        """Returns True if the pool is present
        """
        if os.path.exists("/etc/fdmns/"+self.name):
            return True
        return False

    def is_up(self):
        """Returns True if the fdmn is present and activated
        """
        if not self.has_it():
            return False
        cmd = [ 'showfdmn', self.name ]
        out, err, ret = justcall(cmd)
        if ret != 0:
            if len(err) > 0:
                self.status_log(err)
            return False
        if 'not active' in out:
            return False
        return True

    def do_start(self):
        pass

    def do_stop(self):
        pass

    def sub_devs(self):
        if not os.path.exists(self.sub_devs_name()):
            s = self.svc.group_status(excluded_groups=set(["sync", "hb"]))
            import rcStatus
            if s['overall'].status == rcStatus.UP:
                self.log.debug("no sub_devs cache file and service up ... refresh sub_devs cache")
                self.presync()
            else:
                self.log.debug("no sub_devs cache file and service not up ... unable to evaluate sub_devs")
                return set()
        try:
            with open(self.sub_devs_name(), 'r') as f:
                return set(json.load(f))
        except:
            self.log.error("corrupted sub_devs cache file %s"%self.sub_devs_name())
            raise ex.excError

    def _sub_devs(self):
        # return cache if initialized
        if len(self.sub_devs_cache) > 0 :
            return self.sub_devs_cache

        if not os.path.exists("/etc/fdmns/"+self.name):
            return set()

        import glob
        dl = glob.glob("/etc/fdmns/"+self.name+"/*")
        dl = map(lambda x: os.readlink(x), dl)
        self.sub_devs_cache = set(dl)

        self.log.debug("found sub devs %s held by fdmn %s" % (dl, self.name))
        return self.sub_devs_cache

if __name__ == "__main__":
    p=Disk(name="dom1")
    print(p._sub_devs())
0707010005c9c7000081b40000000000000000000000015ab2175400002d54000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/resDiskVgHP-UX.py  import re
import os
import rcExceptions as ex
import resDisk
from subprocess import *
from rcUtilities import qcall
from rcGlobalEnv import rcEnv

class Disk(resDisk.Disk):
    def __init__(self,
                 rid=None,
                 name=None,
                 dsf=True,
                 **kwargs):
        resDisk.Disk.__init__(self,
                          rid=rid,
                          name=name,
                          type='disk.vg',
                          **kwargs)
        self.label = "vg "+name
        self.dsf = dsf

    def is_child_dev(self, device):
        l = device.split("/")
        if len(l) != 4 or l[1] != "dev":
            return False
        vgname = l[2]
        if vgname == self.name:
            return True
        return False

    def files_to_sync(self):
        return [self.mapfile_name(), self.mksffile_name()]

    def mapfile_name(self):
        return os.path.join(self.var_d, 'map')

    def mksffile_name(self):
        return os.path.join(self.var_d, 'mksf')

    def has_it(self):
        """ returns True if the volume is present
        """
        if self.is_active():
            return True
        if not os.path.exists(self.mapfile_name()):
            return False
        if self.is_imported():
            return True
        return False

    def dev2char(self, dev):
        dev = dev.replace("/dev/disk", "/dev/rdisk")
        dev = dev.replace("/dev/dsk", "/dev/rdsk")
        return dev

    def dsf_name(self, dev):
        cmd = ['scsimgr', 'get_attr', '-D', self.dev2char(dev), '-a', 'device_file', '-p']
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            raise ex.excError
        return out.split()[0]

    def write_mksf(self):
        cmd = ['ioscan', '-F', '-m', 'dsf']
        (ret, buff, err) = self.call(cmd)
        if ret != 0:
            raise ex.excError
        if len(buff) == 0:
            return
        mksf = {}
        devs = self.sub_devs()
        dsf_names = map(self.dsf_name, devs)
        with open(self.mksffile_name(), 'w') as f:
            for line in buff.split('\n'):
                if len(line) == 0:
                    return
                a = line.split(':')[0]
                if '/dev/pt/pt' not in a and '/dev/rdisk/disk' not in a and not a.endswith(".pt") and self.dsf_name(a) in dsf_names:
                    cmd = ['scsimgr', 'get_attr', '-D', self.dev2char(a), '-a', 'wwid', '-p']
                    (ret, out, err) = self.call(cmd)
                    if ret != 0:
                        raise ex.excError
                    f.write(":".join([a, out.split()[0].replace('0x', '')])+'\n')

    def do_mksf(self):
        if not os.path.exists(self.mksffile_name()):
            return

        instance = {}
        cmd = ['scsimgr', 'get_attr', 'all_lun', '-a', 'wwid', '-a', 'instance', '-p']
        (ret, buff, err) = self.call(cmd)
        for line in buff.split('\n'):
            l = line.split(':')
            if len(l) != 2:
                continue
            instance[l[0].replace('0x', '')] = l[1]

        r = 0
        with open(self.mksffile_name(), 'r') as f:
            for line in f.readlines():
                a = line.replace('\n', '').split(':')
                if len(a) == 0:
                    continue
                if os.path.exists(a[0]):
                    continue
                if a[1] not in instance.keys():
                    self.log.error("expected lun %s not present on node %s"%(a[1], rcEnv.nodename))
                    r += 1
                    continue
                cmd = ['mksf', '-r', '-C', 'disk', '-I', instance[a[1]], a[0]]
                (ret, buff, err) = self.vcall(cmd)
                if ret != 0:
                    r += 1
                    continue
        if r > 0:
            raise ex.excError

    def presync(self):
        """ this one is exported as a service command line arg
        """
        cmd = [ 'vgexport', '-m', self.mapfile_name(), '-p', '-s', self.name ]
        ret = qcall(cmd)
        if ret != 0:
            raise ex.excError
        self.write_mksf()

    def is_active(self):
        cmd = [ 'vgdisplay', self.name ]
        process = Popen(cmd, stdout=PIPE, stderr=PIPE, close_fds=True)
        buff = process.communicate()
        if not "available" in buff[0]:
            return False
        return True

    def is_imported(self):
        r = self.is_imported_lvm2()
        if r:
            return True
        return self.is_imported_lvm1()

    def is_imported_lvm2(self):
        if not os.path.exists('/etc/lvmtab_p'):
            return False
        cmd = ['strings', '/etc/lvmtab_p']
        process = Popen(cmd, stdout=PIPE, stderr=PIPE, close_fds=True)
        out, err = process.communicate()
        l = out.split('\n')
        map(lambda x: x.strip(), l)
        s = '/dev/'+self.name
        if s in l:
            return True
        return False

    def is_imported_lvm1(self):
        if not os.path.exists('/etc/lvmtab'):
            return False
        cmd = ['strings', '/etc/lvmtab']
        process = Popen(cmd, stdout=PIPE, stderr=PIPE, close_fds=True)
        out, err = process.communicate()
        l = out.split('\n')
        map(lambda x: x.strip(), l)
        s = '/dev/'+self.name
        if s in l:
            return True
        return False

    def is_up(self):
        """Returns True if the volume group is present and activated
        """
        if not os.path.exists(self.mapfile_name()):
            try:
                self.do_export(force_preview=True)
            except ex.excError:
                # vg does not exist
                return False
        if not self.is_imported():
            return False
        if not self.is_active():
            return False
        return True

    def clean_group(self):
        gp = os.path.join(os.sep, "dev", self.name, "group")
        if not os.path.exists(gp):
            return
        cmd = ["rmsf", gp]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            self.log.error("failed to remove pre-existing %s"%gp)
            raise ex.excError

    def do_import(self):
        if self.is_imported():
            self.log.info("%s is already imported" % self.name)
            return
        if self.dsf:
            dsfflag = '-N'
        else:
            dsfflag = ''
        self.lock()
        self.clean_group()
        cmd = [ 'vgimport', '-m', self.mapfile_name(), '-s', dsfflag, self.name ]
        self.log.info(' '.join(cmd))
        process = Popen(cmd, stdout=PIPE, stderr=PIPE, close_fds=True)
        buff = process.communicate()
        self.unlock()

        # we will modify buff[1], so convert from tuple to list
        buff = list(buff)

        # test string for warnings
        #buff[1] = """Warning: Cannot determine block size of Physical Volume "/dev/rdisk/disk394".
        #Assuming a default value of 1024 bytes. Continuing.
        #Warning: Cannot determine block size of Physical Volume "/dev/rdisk/disk395".
        #Assuming a default value of 1024 bytes. Continuing.
        #vgimport:"""

        if len(buff[1]) > 0:
            import re
            regex = re.compile("Warning:.*\n.*Continuing.\n", re.MULTILINE)
            w = regex.findall(buff[1])
            if len(w) > 0:
                warnings = '\n'.join(w)
                self.log.warning(warnings)
                buff[1] = regex.sub('', buff[1])
            if buff[1] != "vgimport: " and buff[1] != "vgimport:":
                self.log.error('error:\n' + buff[1])

        if len(buff[0]) > 0:
            self.log.debug('output:\n' + buff[0])

        if process.returncode != 0:
            raise ex.excError

    def do_export(self, force_preview=False):
        preview = False
        if os.path.exists(self.mapfile_name()):
            if not self.is_imported():
                self.log.info("%s is already exported" % self.name)
                return
        elif self.is_active():
            preview = True
        if preview or force_preview:
            cmd = [ 'vgexport', '-p', '-m', self.mapfile_name(), '-s', self.name ]
        else:
            cmd = [ 'vgexport', '-m', self.mapfile_name(), '-s', self.name ]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def do_activate(self):
        if self.is_active():
            self.log.info("%s is already available" % self.name)
            return
        cmd = ['vgchange', '-c', 'n', self.name]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError
        cmd = ['vgchange', '-a', 'y', self.name]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def do_deactivate(self):
        if not self.is_active():
            self.log.info("%s is already unavailable" % self.name)
            return
        cmd = ['vgchange', '-a', 'n', self.name]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def do_start(self):
        self.do_import()
        self.do_activate()

    def do_stop(self):
        self.do_deactivate()
        self.do_export()

    def start(self):
        self.do_mksf()
        self.can_rollback = True
        self.do_start()

    def sub_devs(self):
        need_export = False
        if not self.is_active() and not self.is_imported():
            self.do_import()
            need_export = True

        self.sub_devs_cache = set()
        if os.path.exists('/etc/lvmtab'):
            self.sub_devs_cache |= self._sub_devs('/etc/lvmtab')
        if os.path.exists('/etc/lvmtab_p'):
            self.sub_devs_cache |= self._sub_devs('/etc/lvmtab_p')

        if need_export:
            self.do_export()

        return self.sub_devs_cache

    def _sub_devs(self, tabp):
        cmd = ['strings', tabp]
        ret, out, err = self.call(cmd)
        if ret != 0:
            raise ex.excError

        tab = out.split('\n')
        insection = False
        devs = set()
        for e in tab:
            """ move to the first disk of the vg
            """
            if e == "/dev/" + self.name:
                insection = True
                continue
            if not insection:
                continue
            if not  e.startswith('/dev/'):
                continue
            if not e.startswith('/dev/disk') and not e.startswith('/dev/dsk'):
                break
            devs |= set([e])

        return devs

    def lock(self, timeout=30, delay=1):
        import lock
        lockfile = os.path.join(rcEnv.paths.pathlock, 'vgimport')
        lockfd = None
        try:
            lockfd = lock.lock(timeout=timeout, delay=delay, lockfile=lockfile)
        except lock.LockTimeout:
            self.log.error("timed out waiting for lock (%s)"%lockfile)
            raise ex.excError
        except lock.LockNoLockFile:
            self.log.error("lock_nowait: set the 'lockfile' param")
            raise ex.excError
        except lock.LockCreateError:
            self.log.error("can not create lock file %s"%lockfile)
            raise ex.excError
        except lock.LockAcquire as e:
            self.log.warn("another action is currently running (pid=%s)"%e.pid)
            raise ex.excError
        except ex.excSignal:
            self.log.error("interrupted by signal")
            raise ex.excError
        except:
            self.save_exc()
            raise ex.excError("unexpected locking error")
        self.lockfd = lockfd

    def unlock(self):
        import lock
        lock.unlock(self.lockfd)


0707010005c9cc000081b40000000000000000000000015ab21754000011bf000000660000000000000000000000000000002700000000root/usr/share/opensvc/lib/resFsAIX.py    import os

import rcMountsAIX as rcMounts
import resFs as Res
from rcUtilities import qcall, protected_mount, getmount
from rcGlobalEnv import rcEnv
import rcExceptions as ex
from stat import *

def try_umount(self):
    cmd = ['umount', self.mount_point]
    (ret, out, err) = self.vcall(cmd, err_to_info=True)
    if ret == 0:
        return 0

    # don't try to kill process using the source of a
    # protected bind mount
    if protected_mount(self.mount_point):
        return 1

    # best effort kill of all processes that might block
    # the umount operation. The priority is given to mass
    # action reliability, ie don't contest oprator's will
    cmd = ['sync']
    (ret, out, err) = self.vcall(cmd, err_to_info=True)

    for i in range(4):
        cmd = ['fuser', '-k', '-x', '-c', self.mount_point]
        (ret, out, err) = self.vcall(cmd, err_to_info=True)
        self.log.info('umount %s'%self.mount_point)
        cmd = ['umount', self.mount_point]
        ret = qcall(cmd)
        if ret == 0:
            break

    return ret


class Mount(Res.Mount):
    """
    AIX fs resource driver.
    """
    def __init__(self,
                 rid,
                 mount_point,
                 device,
                 fs_type,
                 mount_options,
                 snap_size=None,
                 **kwargs):
        self.mounts = None
        Res.Mount.__init__(self,
                           rid,
                           mount_point=mount_point,
                           device=device,
                           fs_type=fs_type,
                           mount_options=mount_options,
                           snap_size=snap_size,
                           **kwargs)

    def set_fsck_h(self):
        self.fsck_h = {
            'jfs': {
                'bin': 'fsck',
                'cmd': ['fsck', '-p', '-V', 'jfs', self.device]
            },
            'jfs2': {
                'bin': 'fsck',
                'cmd': ['fsck', '-p', '-V', 'jfs2', self.device]
            },
        }

    def is_up(self):
        self.mounts = rcMounts.Mounts()
        return self.mounts.has_mount(self.device, self.mount_point)

    def realdev(self):
        try:
            mode = os.stat(self.device)[ST_MODE]
        except:
            self.log.debug("can not stat %s" % self.device)
            return None
        if S_ISBLK(mode):
            dev = self.device
        else:
            mnt = getmount(self.device)
            if self.mounts is None:
                self.mounts = rcMounts.Mounts()
            m = self.mounts.has_param("mnt", mnt)
            if m is None:
                self.log.debug("can't find dev %(dev)s mounted in %(mnt)s in mnttab"%dict(mnt=mnt, dev=self.device))
                return None
            dev = m.dev

        return dev

    def mplist(self):
        dev = self.realdev()
        if dev is None:
            return set()

        return self._mplist([dev])

    def _mplist(self, devs):
        mps = set()
        return mps

    def sub_devs(self):
        dev = self.realdev()
        if dev is None:
            return set()
        return set()

    def can_check_writable(self):
        return True

    def start(self):
        if self.mounts is None:
            self.mounts = rcMounts.Mounts()
        Res.Mount.start(self)
        if self.is_up() is True:
            self.log.info("%s is already mounted" % self.label)
            return 0
        self.fsck()
        if not os.path.exists(self.mount_point):
            os.makedirs(self.mount_point, 0o755)
        if self.fs_type != "":
            fstype = ['-v', self.fs_type]
        else:
            fstype = []
        if self.mount_options != "":
            mntopt = ['-o', self.mount_options]
        else:
            mntopt = []
        cmd = ['mount']+fstype+mntopt+[self.device, self.mount_point]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError
        self.mounts = None
        self.can_rollback = True

    def stop(self):
        if self.mounts is None:
            self.mounts = rcMounts.Mounts()
        if self.is_up() is False:
            self.log.info("%s is already umounted" % self.label)
            return
        for i in range(3):
            ret = try_umount(self)
            if ret == 0: break
        if ret != 0:
            self.log.error('failed to umount %s'%self.mount_point)
            raise ex.excError
        self.mounts = None

if __name__ == "__main__":
    for c in (Mount,) :
        help(c)

 0707010005c9c8000081b40000000000000000000000015ab2175400002195000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/resDiskVgLinux.py  import re
import os
import rcExceptions as ex
import resDisk
from rcGlobalEnv import rcEnv
from rcUtilitiesLinux import major, get_blockdev_sd_slaves, \
                             devs_to_disks, udevadm_settle
from rcUtilities import which, justcall, cache

class Disk(resDisk.Disk):
    def __init__(self,
                 rid=None,
                 name=None,
                 **kwargs):
        resDisk.Disk.__init__(self,
                              rid=rid,
                              name=name,
                              type='disk.vg',
                              **kwargs)
        self.label = "vg "+name
        self.tag = rcEnv.nodename

    def _info(self):
        data = [
          ["name", self.name],
        ]
        return data

    def is_child_dev(self, device):
        l = device.split("/")
        if len(l) != 4 or l[1] != "dev":
            return False
        if l[2] == "mapper":
            dmname = l[3]
            if "-" not in dmname:
                return False
            i = 0
            dmname.replace("--", "#")
            _l = dmname.split("-")
            if len(_l) != 2:
                return False
            vgname = _l[0].replace("#", "-")
        else:
            vgname = l[2]
        if vgname == self.name:
            return True
        return False

    def has_it(self):
        data = self.get_tags()
        if self.name in data:
            return True
        return False

    def is_up(self):
        """Returns True if the volume group is present and activated
        """
        if not self.has_it():
            return False
        data = self.get_lvs_attr()
        if self.name not in data:
            # no lv ... happens in provisioning, where lv are not created yet
            self.log.debug("no logical volumes. consider up")
            return self.tag in self.get_tags()[self.name]
        for attr in data[self.name].values():
            if re.search('....a.', attr) is not None:
                # at least one lv is active
                return True
        return False

    @cache("vg.lvs.attr")
    def get_lvs_attr(self):
        cmd = [rcEnv.syspaths.lvs, '-o', 'vg_name,lv_name,lv_attr', '--noheadings', '--separator=;']
        out, err, ret = justcall(cmd)
        data = {}
        for line in out.splitlines():
            l = line.split(";")
            if len(l) != 3:
                continue
            vgname = l[0].strip()
            lvname = l[1].strip()
            attr = l[2].strip()
            if vgname not in data:
                data[vgname] = {}
            data[vgname][lvname] = attr
        return data

    @cache("vg.tags")
    def get_tags(self):
        cmd = [rcEnv.syspaths.vgs, '-o', 'vg_name,tags', '--noheadings', '--separator=;']
        out, err, ret = justcall(cmd)
        data = {}
        for line in out.splitlines():
            l = line.split(";")
            if len(l) == 1:
                data[l[0].strip()] = []
            if len(l) == 2:
                data[l[0].strip()] = l[1].strip().split(",")
        return data

    def test_vgs(self):
        data = self.get_tags()
        if self.name not in data:
            self.clear_cache("vg.tags")
            return False
        return True

    def remove_tag(self, tag):
        cmd = ['vgchange', '--deltag', '@'+tag, self.name]
        (ret, out, err) = self.vcall(cmd)
        self.clear_cache("vg.tags")

    def list_tags(self, tags=[]):
        tmo = 5
        try:
            self.wait_for_fn(self.test_vgs, tmo, 1, errmsg="vgs is still reporting the vg as not found after %d seconds"%tmo)
        except ex.excError as e:
            self.log.warning(str(e))
            ret, out, err = self.vcall(["pvscan"])
        # last chance
        data = self.get_tags()
        if self.name not in data:
            raise ex.excError("vg %s not found" % self.name)
        return data[self.name]

    def remove_tags(self, tags=[]):
        for tag in tags:
            tag = tag.lstrip('@')
            if len(tag) == 0:
                continue
            self.remove_tag(tag)

    def add_tags(self):
        cmd = ['vgchange', '--addtag', '@'+self.tag, self.name]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError
        self.clear_cache("vg.tags")

    def activate_vg(self):
        cmd = ['vgchange', '-a', 'y', self.name]
        ret, out, err = self.vcall(cmd)
        self.clear_cache("vg.lvs")
        self.clear_cache("vg.lvs.attr")
        self.clear_cache("vg.tags")
        if ret != 0:
            raise ex.excError

    def _deactivate_vg(self):
        cmd = ['vgchange', '-a', 'n', self.name]
        ret, out, err = self.vcall(cmd, err_to_info=True)
        self.clear_cache("vg.lvs")
        self.clear_cache("vg.lvs.attr")
        self.clear_cache("vg.tags")
        if ret == 0:
            return True
        if not self.is_up():
            return True
        return False

    def deactivate_vg(self):
        self.wait_for_fn(self._deactivate_vg, 3, 1, errmsg="deactivation failed to release all logical volumes")

    def do_start(self):
        self.clear_cache("vg.lvs")
        self.clear_cache("vg.lvs.attr")
        self.clear_cache("vg.tags")
        curtags = self.list_tags()
        tags_to_remove = set(curtags) - set([self.tag])
        if len(tags_to_remove) > 0:
            self.remove_tags(tags_to_remove)
        if self.tag not in curtags:
            self.add_tags()
        if self.is_up():
            self.log.info("%s is already up" % self.label)
            return 0
        self.can_rollback = True
        self.activate_vg()

    def remove_dev_holders(self, devpath, tree):
        dev = tree.get_dev_by_devpath(devpath)
        holders_devpaths = set()
        holder_devs = dev.get_children_bottom_up()
        for holder_dev in holder_devs:
            holders_devpaths |= set(holder_dev.devpath)
        holders_devpaths -= set(dev.devpath)
        holders_handled_by_resources = self.svc.sub_devs() & holders_devpaths
        if len(holders_handled_by_resources) > 0:
            raise ex.excError("resource %s has holders handled by other resources: %s" % (self.rid, ", ".join(holders_handled_by_resources)))
        for holder_dev in holder_devs:
            holder_dev.remove(self)

    def remove_holders(self):
        import glob
        tree = self.svc.node.devtree
        for lvdev in glob.glob("/dev/mapper/%s-*"%self.name.replace("-", "--")):
             if "_rimage_" in lvdev or "_rmeta_" in lvdev or \
                "_mimage_" in lvdev or " _mlog_" in lvdev or \
                lvdev.endswith("_mlog"):
                 continue
             self.remove_dev_holders(lvdev, tree)

    def do_stop(self):
        if not self.is_up():
            self.log.info("%s is already down" % self.label)
            return
        self.remove_holders()
        self.remove_tags([self.tag])
        udevadm_settle()
        self.deactivate_vg()

    @cache("vg.lvs")
    def vg_lvs(self):
        cmd = [rcEnv.syspaths.vgs, '--noheadings', '-o', 'vg_name,lv_name', '--separator', ';']
        out, err, ret = justcall(cmd)
        data = {}
        for line in out.splitlines():
            try:
                vgname, lvname = line.split(";")
                vgname = vgname.strip()
            except:
                pass
            if vgname not in data:
                data[vgname] = []
            data[vgname].append(lvname.strip())
        return data


    @cache("vg.pvs")
    def vg_pvs(self):
        cmd = [rcEnv.syspaths.vgs, '--noheadings', '-o', 'vg_name,pv_name', '--separator', ';']
        out, err, ret = justcall(cmd)
        data = {}
        for line in out.splitlines():
            try:
                vgname, pvname = line.split(";")
                vgname = vgname.strip()
            except:
                pass
            if vgname not in data:
                data[vgname] = []
            data[vgname].append(os.path.realpath(pvname.strip()))
        return data

    def sub_devs(self):
        if not self.has_it():
            return set()
        devs = set()
        data = self.vg_pvs()
        if self.name in data:
            devs |= set(data[self.name])
        return devs

    def exposed_devs(self):
        if not self.has_it():
            return set()
        devs = set()
        data = self.vg_lvs()
        if self.name in data:
            for lvname in data[self.name]:
                lvp = "/dev/"+self.name+"/"+lvname
                if os.path.exists(lvp):
                    devs.add(lvp)
        return devs

   0707010005c9c9000081b40000000000000000000000015ab2175400000c5e000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/resDiskVgVeritas.py    import resDisk
import re

import rcExceptions as ex
from rcUtilities import qcall, which

class Disk(resDisk.Disk):
    """ basic Veritas Volume group resource
    """
    def __init__(self,
                 rid=None,
                 name=None,
                 **kwargs):
        resDisk.Disk.__init__(self,
                              rid=rid,
                              name=name,
                              type='disk.veritas',
                              **kwargs)
        self.label = "vg "+str(name)

    def has_it(self):
        """Returns True if the vg is present
        """
        if not which("vxdg"):
            raise ex.excError("vxdg command not found")
        ret = qcall( [ 'vxdg', 'list', self.name ] )
        if ret == 0 :
            return True
        else:
            return False

    def is_up(self):
        """Returns True if the vg is present and not disabled
        """
        if not which("vxdg"):
            self.status_log("vxdg command not found")
            return False
        if not self.has_it():
            return False
        cmd = [ 'vxprint', '-ng', self.name ]
        ret = qcall(cmd)
        if ret == 0 :
                return True
        else:
                return False

    def do_startvol(self):
        cmd = [ 'vxvol', '-g', self.name, '-f', 'startall' ]
        (ret, out, err) = self.vcall(cmd)
        return ret

    def do_stopvol(self):
        cmd = [ 'vxvol', '-g', self.name, '-f', 'stopall' ]
        (ret, out, err) = self.vcall(cmd)
        return ret

    def do_start(self):
        if self.is_up():
            self.log.info("%s is already up" % self.name)
            ret = self.do_startvol()
            if ret == 0 :
                return 0
            else:
                return ret
        self.can_rollback = True
        for flag in [ '-t', '-tC', '-tCf']:
            cmd = [ 'vxdg', flag, 'import', self.name ]
            (ret, out, err) = self.vcall(cmd)
            if ret == 0 :
                ret = self.do_startvol()
                return ret
        return ret

    def do_stop(self):
        if not self.is_up():
            self.log.info("%s is already down" % self.name)
            return 0
        ret = self.do_stopvol()
        cmd = [ 'vxdg', 'deport', self.name ]
        (ret, out, err) = self.vcall(cmd)
        return ret

    def sub_devs(self):
        """
        parse "vxdisk -g <vgname> -q path"
        """
        if len(self.sub_devs_cache) > 0 :
            return self.sub_devs_cache

        if not which("vxdisk"):
            return set()
        devs = set()
        cmd = [ 'vxdisk', '-g', self.name, '-q', 'list' ]
        (ret, out, err) = self.call(cmd, errlog=False)
        if ret != 0 :
            self.sub_devs_cache = devs
            return devs
        for line in out.split('\n'):
            dev = line.split(" ")[0]
            if dev != '' :
                if re.match('^.*s[0-9]$', dev) is None:
                    dev += "s2"
                devs.add("/dev/rdsk/" + dev)

        self.log.debug("found devs %s held by vg %s" % (devs, self.name))
        self.sub_devs_cache = devs

        return devs

  0707010005c9ca000081b40000000000000000000000015ab2175400001c0d000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/resDiskZfs.py  from rcGlobalEnv import rcEnv
import resDisk
from rcUtilities import qcall, which, lazy, fcache
import os
import rcExceptions as ex
import glob
import json

import re

class Disk(resDisk.Disk):
    """
    Zfs pool resource driver.
    """
    def __init__(self,
                 rid=None,
                 name=None,
                 **kwargs):
        resDisk.Disk.__init__(self,
                              rid=rid,
                              name=name,
                              type='disk.zpool',
                              **kwargs)
        self.label = 'pool ' + name

    def _info(self):
        data = [
          ["name", self.name],
        ]
        return data

    @lazy
    def sub_devs_name(self):
        return os.path.join(self.var_d, 'sub_devs')

    def files_to_sync(self):
        return [self.sub_devs_name]

    def presync(self):
        """ this one is exported as a service command line arg
        """
        if not self.has_it():
            return
        dl = self._sub_devs()
        with open(self.sub_devs_name, 'w') as f:
            f.write(json.dumps(list(dl)))

    def has_it(self):
        """Returns True if the pool is present
        """
        if not which("zpool"):
            raise ex.excError("zpool command not found")
        ret = qcall( [ 'zpool', 'list', self.name ] )
        if ret == 0 :
            return True
        return False

    def is_up(self):
        """Returns True if the pool is present and activated
        """
        if not self.has_it():
            return False
        cmd = [ 'zpool', 'list', '-H', '-o', 'health', self.name ]
        (ret, out, err) = self.call(cmd)
        state = out.strip()
        if state == "ONLINE":
            return True
        elif state == "DEGRADED":
            self.status_log(state)
            return True
        return False

    def do_start(self):
        if self.is_up():
            self.log.info("%s is already up" % self.name)
            return 0
        devzp = os.path.join(self.var_d, 'dev', 'dsk')
        if os.path.isdir(devzp):
            cmd = [ 'zpool', 'import', '-f', '-o', 'cachefile='+os.path.join(rcEnv.paths.pathvar, 'zpool.cache'), '-d', devzp, self.name ]
            (ret, out, err) = self.vcall(cmd)
            if ret == 0:
                return ret
            else:
                self.log.info("import %s: FallBack Long Way" %self.name)
        cmd = [ 'zpool', 'import', '-f', '-o', 'cachefile='+os.path.join(rcEnv.paths.pathvar, 'zpool.cache'), self.name ]
        (ret, out, err) = self.vcall(cmd)
        self.can_rollback = True
        return ret

    def do_stop(self):
        if not self.is_up():
            self.log.info("%s is already down" % self.name)
            return 0
        cmd = [ 'zpool', 'export', self.name ]
        (ret, out, err) = self.vcall(cmd)
        return ret

    def sub_devs(self):
        if self.is_up():
            self.log.debug("resource up ... refresh sub devs cache")
            self.presync()
        elif not os.path.exists(self.sub_devs_name):
            self.log.debug("no sub devs cache file and service not up ... unable to evaluate sub devs")
            return set()
        with open(self.sub_devs_name, 'r') as f:
            buff = f.read()
        try:
            dl = set(json.loads(buff))
        except:
            self.log.error("corrupted sub devs cache file %s"%self.sub_devs_name)
            raise ex.excError
        dl = self.remap_cached_sub_devs_controller(dl)
        return dl

    def remap_cached_sub_devs_controller(self, dl):
        if rcEnv.sysname != "SunOS":
            return dl
        mapping = self.get_wwn_map()
        vdl = []
        for d in dl:
            if os.path.exists(d):
                vdl.append(d)
                continue
            if len(d) < 36 or not d.endswith("s2"):
                self.log.debug("no remapping possible for disk %s. keep as is." % d)
                vdl.append(d)
                continue
            wwid = d[-36:-2]
            if len(wwid) in (18, 26, 34) and wwid.endswith("d0"):
                wwid = wwid[:-2]
                d0 = "d0"
            else:
                d0 = ""
            l = glob.glob("/dev/rdsk/*"+wwid+d0+"s2")
            if len(l) != 1:
                # may be the disk is a R2 mirror member, with a different wwn than R1
                if wwid in mapping:
                    wwid = mapping[wwid]
                    l = glob.glob("/dev/rdsk/*"+wwid+d0+"s2")
            if len(l) != 1:
                self.log.warning("discard disk %s from sub devs cache: "
                                 "not found", wwid)
                continue
            self.log.debug("remapped device %s to %s" % (d, l[0]))
            vdl.append(l[0])
        return set(vdl)

    def get_wwn_map(self):
        mapping = {}
        wwn_maps = glob.glob(os.path.join(self.svc.var_d, "*", "wwn_map"))
        for fpath in wwn_maps:
            try:
                with open(fpath, "r") as filep:
                    _mapping = json.load(filep)
            except ValueError:
                pass
            else:
                for (r1, r2) in _mapping:
                    mapping[r1] = r2
                    mapping[r2] = r1
        return mapping

    @fcache
    def _sub_devs(self):
        """
        Search zpool vdevs from the output of "zpool status poolname" if
        imported else from the output of "zpool import".
        """
        devs = set()
        cmd = ['zpool', 'status']
        if rcEnv.sysname == "Linux":
            cmd += ["-L", "-P"]
        cmd += [self.name]
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            raise ex.excError

        for line in out.split('\n'):
            if re.match('^\t  ', line) is not None:
                if re.match('^\t  mirror', line) is not None:
                    continue
                if re.match('^\t  raid', line) is not None:
                    continue
                # vdev entry
                disk = line.split()[0]
                if rcEnv.sysname == "SunOS":
                    if disk.startswith(rcEnv.paths.pathvar):
                        disk = disk.split('/')[-1]
                    if re.match("^.*", disk) is None:
                        continue
                    if not disk.startswith("/dev/rdsk/"):
                        disk = "/dev/rdsk/" + disk
                devs.add(disk)

        vdevs = set()
        for d in devs:
            if "emcpower" in d:
                regex = re.compile('[a-g]$', re.UNICODE)
                d = regex.sub('c', d)
            elif rcEnv.sysname == "SunOS":
                if re.match('^.*s[0-9]*$', d) is None:
                    d += "s2"
                else:
                    regex = re.compile('s[0-9]*$', re.UNICODE)
                    d = regex.sub('s2', d)
            vdevs.add(d)

        return vdevs

if __name__ == "__main__":
    help(Disk)

    # return cache if initialized
    print("""p=Disk("svczfs1")""")
    p=Disk("svczfs1")
    print("show p", p)
    print("""p.do_action("start")""")
    p.do_action("start")
    print("""p.do_action("stop")""")
    p.do_action("stop")
   0707010005c9cd000081b40000000000000000000000015ab2175400001b02000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/resFsDarwin.py import os

import rcMountsDarwin as rcMounts
import resFs as Res
from rcUtilities import qcall, protected_mount, getmount
from rcGlobalEnv import rcEnv
from rcLoopDarwin import file_to_loop
import rcExceptions as ex
from stat import *

def try_umount(self):
    cmd = ['diskutil', 'umount', self.mount_point]
    (ret, out, err) = self.vcall(cmd, err_to_info=True)
    if ret == 0:
        return 0

    cmd = ['diskutil', 'umount', 'force', self.mount_point]
    (ret, out, err) = self.vcall(cmd, err_to_info=True)
    if ret == 0:
        return 0

    cmd = ['umount', self.mount_point]
    (ret, out, err) = self.vcall(cmd, err_to_info=True)
    if ret == 0:
        return 0

    """ don't try to kill process using the source of a
        protected bind mount
    """
    if protected_mount(self.mount_point):
        return 1

    """ best effort kill of all processes that might block
        the umount operation. The priority is given to mass
        action reliability, ie don't contest oprator's will
    """
    cmd = ['sync']
    (ret, out, err) = self.vcall(cmd, err_to_info=True)

    for i in range(4):
        nb_killed = self.killfuser(self.mount_point)
        self.log.info('umount %s'%self.mount_point)
        cmd = ['umount', self.mount_point]
        ret = qcall(cmd)
        if ret == 0 or nb_killed == 0:
            break

    if ret != 0:
        self.log.info("no more process using %s, yet umount fails. try forced umount."%self.mount_point)
        cmd = ['umount', '-f', self.mount_point]
        (ret, out, err) = self.vcall(cmd, err_to_info=True)

    return ret


class Mount(Res.Mount):
    """ define FreeBSD mount/umount doAction """
    def __init__(self,
                 rid,
                 mount_point,
                 device,
                 fs_type,
                 mount_options,
                 snap_size=None,
                 **kwargs):
        self.Mounts = None
        self.loopdevice = None
        self.isloop = False
        Res.Mount.__init__(self,
                           rid,
                           mount_point=mount_point,
                           device=device,
                           fs_type=fs_type,
                           mount_options=mount_options,
                           snap_size=snap_size,
                           **kwargs)

    def set_fsck_h(self):
        self.fsck_h = {
            'hfs': {
                'bin': 'fsck',
                'cmd': ['diskutil', 'repairVolume', self.device]
            },
        }

    def killfuser(self, dir):
        cmd = ['fuser', '-kmc', dir]
        (ret, out, err) = self.vcall(cmd, err_to_info=True)

        """ return the number of process we sent signal to
        """
        l = out.split(':')
        if len(l) < 2:
            return 0
        return len(l[1].split())

    def is_up(self):
        self.Mounts = rcMounts.Mounts()
        ret = self.Mounts.has_mount(self.device, self.mount_point)
        if ret:
            return True

        if self.fs_type not in self.netfs:
            try:
                st = os.stat(self.device)
                mode = st[ST_MODE]
            except:
                self.log.debug("can not stat %s" % self.device)
                return False

            if S_ISREG(mode):
                # might be a loopback mount
                devs = file_to_loop(self.device)
                for dev in devs:
                    ret = self.Mounts.has_mount(dev, self.mount_point)
                    if ret:
                        return True

        return False

    def realdev(self):
        dev = None
        try:
            mode = os.stat(self.device)[ST_MODE]
        except:
            self.log.debug("can not stat %s" % self.device)
            return None
        if S_ISCHR(mode):
            dev = self.device
        else:
            mnt = getmount(self.device)
            if self.Mounts is None:
                self.Mounts = rcMounts.Mounts()
            m = self.Mounts.has_param("mnt", mnt)
            if m is None:
                self.log.debug("can't find dev %(dev)s mounted in %(mnt)s in mnttab"%dict(mnt=mnt, dev=self.device))
                return None
            dev = m.dev

        return dev

    def sub_devs(self):
        dev = self.realdev()
        if dev is None:
            return set()

        try:
            statinfo = os.stat(dev)
        except:
            self.log.error("can not stat %s" % dev)
            raise ex.excError

        return set([dev])

    def can_check_writable(self):
        return True

    def start(self):
        if self.Mounts is None:
            self.Mounts = rcMounts.Mounts()
        Res.Mount.start(self)

        if self.fs_type in self.netfs or self.device == "none":
            # TODO showmount -e
            pass
        else:
            try:
                mode = os.stat(self.device)[ST_MODE]
                if S_ISREG(mode):
                    devs = file_to_loop(self.device)
                    if len(devs) > 0:
                        self.loopdevice = devs[0]
                    self.loopdev = devs[0]
                    self.isloop = True
            except:
                self.log.debug("can not stat %s" % self.device)
                return False

        if self.is_up() is True:
            self.log.info("%s is already mounted" % self.label)
            return 0

        if not os.path.exists(self.mount_point):
            os.makedirs(self.mount_point, 0o755)

        if self.isloop is True:
            #cmd = ['hdiutil', 'attach', '-mountpoint', self.mount_point , self.device]
            #(ret, out, err) = self.vcall(cmd)
            device = self.loopdev
        else:
            device = self.device
            self.fsck()

        try:
            cmd = ['diskutil', 'mount', '-mountPoint', self.mount_point , device]
            (ret, out, err) = self.vcall(cmd)
        except:
            if self.fs_type != "":
                fstype = ['-t', self.fs_type]
            else:
                fstype = []
            if self.mount_options != "":
                mntopt = ['-o', self.mount_options]
            else:
                mntopt = []
            cmd = ['mount']+fstype+mntopt+[self.device, self.mount_point]
            (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError
        self.Mounts = None
        self.can_rollback = True

    def stop(self):
        if self.Mounts is None:
            self.Mounts = rcMounts.Mounts()
        if self.is_up() is False:
            self.log.info("%s is already umounted" % self.label)
            return
        for i in range(3):
            ret = try_umount(self)
            if ret == 0: break
        if ret != 0:
            self.log.error('failed to umount %s'%self.mount_point)
            raise ex.excError
        self.Mounts = None

if __name__ == "__main__":
    for c in (Mount,) :
        help(c)

  0707010005c9ce000081b40000000000000000000000015ab2175400000ed6000000660000000000000000000000000000002700000000root/usr/share/opensvc/lib/resFsDir.py    import resources as Res
import os
import rcExceptions as ex
import rcStatus
from rcGlobalEnv import rcEnv
from rcUtilities import is_string, lazy
import pwd
import grp
import stat

class FsDir(Res.Resource):
    """Define a mount resource
    """

    def __init__(self,
                 rid=None,
                 path=None,
                 user=None,
                 group=None,
                 perm=None,
                 **kwargs):
        Res.Resource.__init__(self,
                              rid=rid,
                              type="fs",
                              **kwargs)
        self.type = "fs.dir"
        self.path = path
        self.mount_point = path # for fs ordering
        self.user = user
        self.group = group
        self.perm = perm

    @lazy
    def label(self):
        return "dir " + self.path

    def start(self):
        self.create()

    def get_gid(self):
        if is_string(self.group):
            info = grp.getgrnam(self.group)
            self.gid = info[2]
        else:
            self.gid = int(self.group)

    def get_uid(self):
        if is_string(self.user):
            info = pwd.getpwnam(self.user)
            self.uid = info[2]
        else:
            self.uid = int(self.user)

    def create(self):
        if not os.path.exists(self.path):
            self.log.info("create directory %s" % (self.path))
            os.makedirs(self.path)
        if not self.check_uid():
            self.log.info("set %s user to %s" % (self.path, str(self.user)))
            os.chown(self.path, self.uid, -1)
        if not self.check_gid():
            self.log.info("set %s group to %s" % (self.path, str(self.group)))
            os.chown(self.path, -1, self.gid)
        if not self.check_perm():
            self.log.info("set %s perm to %s" % (self.path, str(self.perm)))
            os.chmod(self.path, int(str(self.perm), 8))

    def check_uid(self):
        if self.user is None:
            return True
        if not os.path.exists(self.path):
            return True
        self.get_uid()
        uid = os.stat(self.path).st_uid
        if uid != self.uid:
            self.status_log('uid should be %s but is %s'%(str(self.uid), str(uid)))
            return False
        return True

    def check_gid(self):
        if self.group is None:
            return True
        if not os.path.exists(self.path):
            return True
        self.get_gid()
        gid = os.stat(self.path).st_gid
        if gid != self.gid:
            self.status_log('gid should be %s but is %s'%(str(self.gid), str(gid)))
            return False
        return True

    def check_perm(self):
        if self.perm is None:
            return True
        if not os.path.exists(self.path):
            return True
        perm = oct(stat.S_IMODE(os.stat(self.path).st_mode))
        perm = str(perm).lstrip("0o").lstrip("0")
        if perm != str(self.perm):
            self.status_log('perm should be %s but is %s'%(str(self.perm), perm))
            return False
        return True

    def _status(self, verbose=False):
        if not os.path.exists(self.path):
            self.log.debug("dir %s does not exist" % self.path)
            return rcStatus.DOWN
        self.check_uid()
        self.check_gid()
        self.check_perm()
        if self.status_logs_count(["warn", "error"]) > 0:
            return rcStatus.WARN
        else:
            return rcStatus.NA

    def __str__(self):
        return "%s path=%s user=%s group=%s perm=%s" % (Res.Resource.__str__(self),\
                self.path, str(self.user), str(self.group), str(self.perm))

    def __lt__(self, other):
        """
        Order so that deepest mountpoint can be umount first
        """
        return self.mount_point < other.mount_point




  0707010005c9cf000081b40000000000000000000000015ab2175400000727000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/resFsDocker.py import os
import rcExceptions as ex
import resources
from rcGlobalEnv import rcEnv
from rcUtilities import lazy

class Fs(resources.Resource):
    def __init__(self,
                 rid=None,
                 driver=None,
                 options=None,
                 **kwargs):
        resources.Resource.__init__(self,
                                    rid=rid,
                                    type='fs.docker',
                                    **kwargs)
        self.driver = driver
        self.options = options

    @lazy
    def label(self):
        return "%s volume %s" % (self.driver, self.volname)

    def _info(self):
        data = [
          ["name", self.volname],
          ["driver", self.driver],
          ["options", self.options],
          ["vol_path", self.vol_path],
        ]
        return data

    @lazy
    def vol_path(self):
        return self.svc.dockerlib.docker_volume_inspect(self.volname).get("Mountpoint")

    def has_it(self):
        try:
            data = self.svc.dockerlib.docker_volume_inspect(self.volname)
            return True
        except (ValueError, IndexError):
            return False

    def is_up(self):
        """
        Returns True if the logical volume is present and activated
        """
        return self.has_it()

    @lazy
    def volname(self):
        return ".".join([self.svc.svcname, self.rid.replace("#", ".")])

    def create_vol(self):
        if self.has_it():
            return 0
        cmd = self.svc.dockerlib.docker_cmd + ["volume", "create", "--name", self.volname]
        if self.options:
            cmd += options
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def start(self):
        self.create_vol()
        self.can_rollback = True

    def stop(self):
        pass

 0707010005c9d0000081b40000000000000000000000015ab21754000012f0000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/resFsFreeBSD.py    import os

import rcMountsFreeBSD as rcMounts
import resFs as Res
from rcUtilities import qcall, protected_mount, getmount
from rcGlobalEnv import rcEnv
import rcExceptions as ex
from stat import *

def try_umount(self):
    cmd = ['umount', self.mount_point]
    (ret, out, err) = self.vcall(cmd, err_to_info=True)
    if ret == 0:
        return 0

    """ don't try to kill process using the source of a
        protected bind mount
    """
    if protected_mount(self.mount_point):
        return 1

    """ best effort kill of all processes that might block
        the umount operation. The priority is given to mass
        action reliability, ie don't contest oprator's will
    """
    cmd = ['sync']
    (ret, out, err) = self.vcall(cmd, err_to_info=True)

    for i in range(4):
        nb_killed = self.killfuser(self.mount_point)
        self.log.info('umount %s'%self.mount_point)
        cmd = ['umount', self.mount_point]
        ret = qcall(cmd)
        if ret == 0 or nb_killed == 0:
            break

    if ret != 0:
        self.log.info("no more process using %s, yet umount fails. try forced umount."%self.mount_point)
        cmd = ['umount', '-f', self.mount_point]
        (ret, out, err) = self.vcall(cmd, err_to_info=True)

    return ret


class Mount(Res.Mount):
    """ define FreeBSD mount/umount doAction """
    def __init__(self,
                 rid,
                 mount_point,
                 device,
                 fs_type,
                 mount_options,
                 snap_size=None,
                 **kwargs):
        self.Mounts = None
        Res.Mount.__init__(self,
                           rid,
                           mount_point,
                           device,
                           fs_type,
                           mount_options,
                           snap_size,
                           **kwargs)

    def set_fsck_h(self):
        self.fsck_h = {
            'ufs': {
                'bin': 'fsck',
                'cmd': ['fsck', '-t', 'ufs', '-p', self.device]
            },
        }

    def killfuser(self, dir):
        cmd = ['fuser', '-kmc', dir]
        (ret, out, err) = self.vcall(cmd, err_to_info=True)

        """ return the number of process we sent signal to
        """
        l = out.split(':')
        if len(l) < 2:
            return 0
        return len(l[1].split())

    def is_up(self):
        self.Mounts = rcMounts.Mounts()
        return self.Mounts.has_mount(self.device, self.mount_point)

    def realdev(self):
        dev = None
        try:
            mode = os.stat(self.device)[ST_MODE]
        except:
            self.log.debug("can not stat %s" % self.device)
            return None
        if S_ISCHR(mode):
            dev = self.device
        else:
            mnt = getmount(self.device)
            if self.Mounts is None:
                self.Mounts = rcMounts.Mounts()
            m = self.Mounts.has_param("mnt", mnt)
            if m is None:
                self.log.debug("can't find dev %(dev)s mounted in %(mnt)s in mnttab"%dict(mnt=mnt, dev=self.device))
                return None
            dev = m.dev

        return dev

    def sub_devs(self):
        dev = self.realdev()
        if dev is None:
            return set()

        try:
            statinfo = os.stat(dev)
        except:
            self.log.error("can not stat %s" % dev)
            raise ex.excError

        return set([dev])

    def can_check_writable(self):
        return True

    def start(self):
        if self.Mounts is None:
            self.Mounts = rcMounts.Mounts()
        Res.Mount.start(self)
        if self.is_up() is True:
            self.log.info("%s is already mounted" % self.label)
            return 0
        self.fsck()
        if not os.path.exists(self.mount_point):
            os.makedirs(self.mount_point, 0o755)
        if self.fs_type != "":
            fstype = ['-t', self.fs_type]
        else:
            fstype = []
        if self.mount_options != "":
            mntopt = ['-o', self.mount_options]
        else:
            mntopt = []
        cmd = ['mount']+fstype+mntopt+[self.device, self.mount_point]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError
        self.Mounts = None
        self.can_rollback = True

    def stop(self):
        if self.Mounts is None:
            self.Mounts = rcMounts.Mounts()
        if self.is_up() is False:
            self.log.info("%s is already umounted" % self.label)
            return
        for i in range(3):
            ret = try_umount(self)
            if ret == 0: break
        if ret != 0:
            self.log.error('failed to umount %s'%self.mount_point)
            raise ex.excError
        self.Mounts = None

if __name__ == "__main__":
    for c in (Mount,) :
        help(c)

0707010005c9d1000081b40000000000000000000000015ab2175400000c43000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/resFsHP-UX.py  import os

import rcStatus
from rcGlobalEnv import rcEnv
rcMounts = __import__('rcMounts'+rcEnv.sysname)
import resFs as Res
from rcUtilities import qcall, protected_mount
import rcExceptions as ex

def try_umount(self):
    cmd = ['umount', self.mount_point]
    (ret, out, err) = self.vcall(cmd, err_to_info=True)
    if ret == 0:
        return 0

    """ don't try to kill process using the source of a
        protected bind mount
    """
    if protected_mount(self.mount_point):
        return 1

    """ best effort kill of all processes that might block
        the umount operation. The priority is given to mass
        action reliability, ie don't contest oprator's will
    """
    cmd = ['sync']
    (ret, out, err) = self.vcall(cmd, err_to_info=True)

    for i in range(4):
        cmd = ['fuser', '-kc', self.mount_point]
        (ret, out, err) = self.vcall(cmd, err_to_info=True)
        self.log.info('umount %s'%self.mount_point)
        cmd = ['umount', self.mount_point]
        ret = qcall(cmd)
        if ret == 0:
            break

    return ret


class Mount(Res.Mount):
    """ define HP-UX mount/umount doAction """
    def __init__(self,
                 rid,
                 mount_point,
                 device,
                 fs_type,
                 mount_options,
                 snap_size=None,
                 **kwargs):
        Res.Mount.__init__(self,
                           rid,
                           mount_point=mount_point,
                           device=device,
                           fs_type=fs_type,
                           mount_options=mount_options,
                           snap_size=snap_size,
                           **kwargs)

    def set_fsck_h(self):
        self.fsck_h = {
            'vxfs': {
                'bin': 'fsck',
                'cmd': ['fsck', '-F', 'vxfs', '-y', self.device]
            },
        }

    def is_up(self):
        return rcMounts.Mounts().has_mount(self.device, self.mount_point)

    def start(self):
        Res.Mount.start(self)
        if self.is_up() is True:
            self.log.info("%s is already mounted" % self.label)
            return 0
        self.fsck()
        if not os.path.exists(self.mount_point):
            os.makedirs(self.mount_point, 0o755)
        if self.fs_type != "":
            fstype = ['-F', self.fs_type]
        else:
            fstype = []
        if self.mount_options != "":
            mntopt = ['-o', self.mount_options]
        else:
            mntopt = []
        cmd = ['mount']+fstype+mntopt+[self.device, self.mount_point]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError
        self.can_rollback = True

    def stop(self):
        if self.is_up() is False:
            self.log.info("%s is already umounted" % self.label)
            return 0
        for i in range(3):
            ret = try_umount(self)
            if ret == 0: break
        if ret != 0:
            self.log.error('failed to umount %s'%self.mount_point)
            raise ex.excError

if __name__ == "__main__":
    for c in (Mount,) :
        help(c)

 0707010005c9d2000081b40000000000000000000000015ab2175400004e7c000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/resFsLinux.py  """
Linux Fs resource driver module
"""
import os
from stat import ST_MODE, ST_INO, S_ISREG, S_ISBLK, S_ISDIR

from rcGlobalEnv import rcEnv
import rcMountsLinux as rcMounts
import resFs as Res
from rcUtilities import qcall, protected_mount, getmount, justcall, lazy, cache
from rcUtilitiesLinux import major, get_blockdev_sd_slaves, lv_exists, devs_to_disks, label_to_dev
from rcLoopLinux import file_to_loop, loop_to_file
import rcExceptions as ex
from rcZfs import zfs_getprop, zfs_setprop

class Mount(Res.Mount):
    """
    Linux Fs resource driver
    """
    def __init__(self, **kwargs):
        self.mounts = None
        Res.Mount.__init__(self, **kwargs)
        self.loopdevice = None

    def set_fsck_h(self):
        # 0    - No errors
        # 1    - File system errors corrected
        # 32   - E2fsck canceled by user request
        self.fsck_h = {
            'ext2': {
                'bin': 'e2fsck',
                'cmd': ['e2fsck', '-p', self.device],
                'allowed_ret': [0, 1, 32, 33]
            },
            'ext3': {
                'bin': 'e2fsck',
                'cmd': ['e2fsck', '-p', self.device],
                'allowed_ret': [0, 1, 32, 33]
            },
            'ext4': {
                'bin': 'e2fsck',
                'cmd': ['e2fsck', '-p', self.device],
                'allowed_ret': [0, 1, 32, 33]
            },
        }

    @lazy
    def device(self):
        if self._device is not None:
            device = self._device
        else:
            # lazy reference support
            device = self.conf_get("dev")
        if device is None:
            return device
        dev_realpath = os.path.realpath(device)
        if device.startswith("/dev/disk/by-") or dev_realpath.startswith("/dev/rbd"):
            device = dev_realpath
        return device

    def umount_generic(self, mnt=None):
        if mnt is None:
            mnt = self.mount_point
        cmd = ['umount', mnt]
        return self.vcall(cmd, err_to_warn=True)

    def try_umount(self, mnt=None):
        if mnt is None:
            mnt = self.mount_point

        if self.fs_type == "zfs":
            ret, out, err = self.umount_zfs()
        else:
            ret, out, err = self.umount_generic(mnt)

        if ret == 0:
            return 0

        if "not mounted" in err:
            return 0

        # don't try to kill process using the source of a
        # protected bind mount
        if protected_mount(mnt):
            return 1

        # best effort kill of all processes that might block
        # the umount operation. The priority is given to mass
        # action reliability, ie don't contest oprator's will
        cmd = ['sync']
        ret, out, err = self.vcall(cmd)

        if os.path.isdir(self.device):
            fuser_opts = '-kv'
        else:
            fuser_opts = '-kmv'
        for _ in range(4):
            cmd = ['fuser', fuser_opts, mnt]
            (ret, out, err) = self.vcall(cmd, err_to_info=True)
            self.log.info('umount %s', mnt)
            cmd = ['umount', mnt]
            ret = qcall(cmd)
            if ret == 0:
                break

        return ret

    def is_up(self):
        if self.device is None:
            return False
        self.mounts = rcMounts.Mounts()
        ret = self.mounts.has_mount(self.device, self.mount_point)
        if ret:
            return True

        # might be defined as a symlink. Linux display realpaths in /proc/mounts
        ret = self.mounts.has_mount(self.device,
                                    os.path.realpath(self.mount_point))
        if ret:
            return True

        # might be defined as a symlink. Linux display realpaths in /proc/mounts
        ret = self.mounts.has_mount(os.path.realpath(self.device),
                                    os.path.realpath(self.mount_point))
        if ret:
            return True

        # might be a loop device seen in mounts as its backing file
        if self.device.startswith("/dev/loop"):
            backfile = loop_to_file(self.device)
            if backfile and self.mounts.has_mount(backfile, os.path.realpath(self.mount_point)):
                return True

        # might be a mount by label or uuid
        for dev in self.sub_devs():
            ret = self.mounts.has_mount(dev, self.mount_point)
            if ret:
                return True
            ret = self.mounts.has_mount(dev, os.path.realpath(self.mount_point))
            if ret:
                return True
            if dev.startswith("/dev/loop"):
                backfile = loop_to_file(dev)
                if backfile and self.mounts.has_mount(backfile, os.path.realpath(self.mount_point)):
                    return True

        # might be mount using a /dev/mapper/ name too
        elements = self.device.split('/')
        if len(elements) == 4 and elements[2] != "mapper":
            dev = "/dev/mapper/%s-%s" % (elements[2].replace('-', '--'),
                                         elements[3].replace('-', '--'))
            ret = self.mounts.has_mount(dev, self.mount_point)
            if ret:
                return True
            ret = self.mounts.has_mount(dev, os.path.realpath(self.mount_point))
            if ret:
                return True

        if os.path.exists(self.device):
            try:
                fstat = os.stat(self.device)
                mode = fstat[ST_MODE]
            except:
                self.log.debug("can not stat %s", self.device)
                return False

            if S_ISREG(mode):
                # might be a loopback mount
                devs = file_to_loop(self.device)
                for dev in devs:
                    ret = self.mounts.has_mount(dev, self.mount_point)
                    if ret:
                        return True
                    ret = self.mounts.has_mount(dev, os.path.realpath(self.mount_point))
                    if ret:
                        return True
            elif S_ISBLK(mode):
                # might be a mount using a /dev/dm-<minor> name too
                if os.major(fstat.st_rdev) == self.dm_major:
                    dev = '/dev/dm-' + str(os.minor(fstat.st_rdev))
                    ret = self.mounts.has_mount(dev, self.mount_point)
                    if ret:
                        return True
                    ret = self.mounts.has_mount(dev, os.path.realpath(self.mount_point))
                    if ret:
                        return True
            elif S_ISDIR(mode):
                try:
                    mnt_fstat = os.stat(self.mount_point)
                    mnt_ino = mnt_fstat[ST_INO]
                except:
                    self.log.debug("can not stat %s", self.mount_point)
                    return False
                dev_ino = fstat[ST_INO]
                if dev_ino == mnt_ino:
                    return True

        return False

    def realdev(self):
        if self.device.startswith("LABEL=") or self.device.startswith("UUID="):
            try:
                _dev = label_to_dev(self.device, self.svc.node.devtree)
            except ex.excError as exc:
                self.status_log(str(exc))
                _dev = None
            if _dev:
                return _dev
            return self.device
        try:
            mode = os.stat(self.device)[ST_MODE]
        except:
            self.log.debug("can not stat %s", self.device)
            return

        if os.path.exists(self.device) and S_ISBLK(mode):
            dev = self.device
        else:
            mnt = getmount(self.device)
            if self.mounts is None:
                self.mounts = rcMounts.Mounts()
            mount = self.mounts.has_param("mnt", mnt)
            if mount is None:
                self.log.debug("can't find dev %s mounted in %s in mnttab",
                               mnt, self.device)
                return None
            dev = mount.dev

        return dev

    @cache("dmsetup.ls.multipath")
    def dmsetup_ls_multipath(self):
        cmd = [rcEnv.syspaths.dmsetup, "ls", "--target", "multipath"]
        out, _, _ = justcall(cmd)
        data = {}
        for line in out.splitlines():
            try:
                name, devno = line.replace(", ", ":").split()
            except Exception:
                continue
            data[name] = devno.strip("()")
        return data

    @lazy
    def dmsetup_ls_multipath_rev(self):
        data = {}
        for k, v in self.dmsetup_ls_multipath().items():
            data[v] = k
        return data

    @cache("dmsetup.status")
    def dmsetup_status(self):
        cmd = [rcEnv.syspaths.dmsetup, "status"]
        out, _, _ = justcall(cmd)
        data = {}
        for line in out.splitlines():
            v = line.split()
            data[v[0]] = line[len(v[0]):].strip()
        return data

    @cache("dmsetup.table")
    def dmsetup_table(self):
        cmd = [rcEnv.syspaths.dmsetup, "table"]
        out, _, _ = justcall(cmd)
        data = {}
        for line in out.splitlines():
            v = line.split()
            data[v[0]] = line[len(v[0]):].strip()
        return data

    @lazy
    def dm_major(self):
        try:
            return major('device-mapper')
        except:
            return

    def mplist(self):
        dev = self.realdev()
        if dev is None:
            return set()
        if self.dm_major is None:
            return set()
        return self._mplist([dev])

    @staticmethod
    def devname_to_dev(devname):
        if 'cciss!' in devname:
            return '/dev/cciss/'+devname.replace('cciss!', '')
        return '/dev/'+devname

    def _mplist(self, devs):
        mps = set()
        for dev in devs:
            devmap = False
            if 'dm-' in dev:
                minor = int(dev.replace('/dev/dm-', ''))
                devname = dev.replace('/dev/', '')
                devmap = True
            else:
                try:
                    statinfo = os.stat(dev)
                except:
                    self.log.debug("can not stat %s", dev)
                    continue
                minor = os.minor(statinfo.st_rdev)
                devname = 'dm-%i'%minor
                devmap = self.is_devmap(statinfo)

            if self.is_multipath(minor):
                mps |= set([dev])
            elif devmap:
                syspath = '/sys/block/' + devname + '/slaves'
                if not os.path.exists(syspath):
                    continue
                slaves = os.listdir(syspath)
                mps |= self._mplist([self.devname_to_dev(slave) for slave in slaves])
        return mps

    def is_multipath(self, minor):
        devno = "%d:%d" % (self.dm_major, minor)
        if devno not in self.dmsetup_ls_multipath_rev:
            return False
        name = self.dmsetup_ls_multipath_rev[devno]
        dmsetup_table = self.dmsetup_table()
        if name not in dmsetup_table:
            return False
        elements = dmsetup_table[name].split()
        if 'queue_if_no_path' not in elements:
            return False
        dmsetup_status = self.dmsetup_status()
        if name not in dmsetup_status:
            return False
        elements = dmsetup_status[name].split()
        if elements.count('A') > 1:
            return False
        return True

    def is_devmap(self, statinfo):
        if os.major(statinfo.st_rdev) == self.dm_major:
            return True
        return False

    def sub_devs(self):
        if self.fs_type == "btrfs":
            from rcBtrfs import btrfs_devs
            return set(btrfs_devs(self.mount_point))

        dev = self.realdev()
        if dev is None or dev.startswith("LABEL=") or dev.startswith("UUID="):
            # realdev() may fail to resolve UUID and LABEL if the hosting dev
            # is not visible
            return set()

        if dev.startswith("/dev/rbd") or dev.startswith("/dev/loop"):
            return set([dev])

        if self.dm_major is None:
            return set([dev])

        try:
            statinfo = os.stat(dev)
        except:
            self.log.error("can not stat %s", dev)
            raise ex.excError

        if not self.is_devmap(statinfo):
            return set([dev])

        if lv_exists(self, dev):
            # If the fs is built on a lv of a private vg, its
            # disks will be given by the vg resource.
            # if the fs is built on a lv of a shared vg, we
            # don't want to account its disks : don't reserve
            # them, don't account their size multiple times.
            return set()

        devname = 'dm-' + str(os.minor(statinfo.st_rdev))
        syspath = '/sys/block/' + devname + '/slaves'
        devs = get_blockdev_sd_slaves(syspath)
        return devs

    def sub_disks(self):
        return devs_to_disks(self, self.sub_devs())

    def can_check_writable(self):
        if self.fs_type == "zfs":
            return self._can_check_zfs_writable()
        if len(self.mplist()) > 0:
            self.log.debug("a multipath under fs has queueing enabled and no active path")
            return False
        return True

    def set_loopdevice(self):
        # loopback mount
        # if the file has already been binded to a loop re-use
        # the loopdev to avoid allocating another one
        if not os.path.exists(self.device):
            return
        if not os.path.isfile(self.device):
            return
        try:
            devs = file_to_loop(self.device)
            if len(devs) > 0:
                self.loopdevice = devs[0]
                mntopt_l = self.mount_options.split(',')
                if "loop" in mntopt_l:
                    mntopt_l.remove("loop")
                    self.mount_options = ','.join(mntopt_l)
        except Exception as exc:
            raise ex.excError(str(exc))

    def start(self):
        if self.mounts is None:
            self.mounts = rcMounts.Mounts()
        Res.Mount.start(self)

        self.set_loopdevice()

        if self.fs_type == "zfs":
            self._check_zfs_canmount()

        if self.is_up() is True:
            self.log.info("%s is already mounted", self.label)
            return 0

        if self.fs_type == "btrfs":
            cmd = ['btrfs', 'device', 'scan']
            self.vcall(cmd)

        self.fsck()
        if not os.path.exists(self.mount_point):
            try:
                os.makedirs(self.mount_point, 0o755)
            except Exception as exc:
                raise ex.excError(str(exc))

        if self.fs_type == "zfs":
            self.mount_zfs()
        else:
            self.mount_generic()

        self.mounts = None
        self.can_rollback = True

    def _can_check_zfs_writable(self):
        pool = self.device.split("/")[0]
        cmd = [rcEnv.syspaths.zpool, "status", pool]
        out, err, ret = justcall(cmd)
        if "state: SUSPENDED" in out:
            self.status_log("pool %s is suspended" % pool)
            return False
        return True

    def _check_zfs_canmount(self):
        if 'noaction' not in self.tags and \
           zfs_getprop(self.device, 'canmount') != 'noauto':
            self.log.info("%s should be set to canmount=noauto (zfs set "
                          "canmount=noauto %s)", self.label, self.device)

    def umount_zfs(self):
        ret, out, err = self.vcall([rcEnv.syspaths.zfs, 'umount', self.device], err_to_info=True)
        if ret != 0:
            ret, out, err = self.vcall([rcEnv.syspaths.zfs, 'umount', '-f', self.device], err_to_info=True)
        return ret, out, err

    def mount_zfs(self):
        if not self.encap and \
           not self.svc.config.has_option(self.rid, 'zone') and \
           zfs_getprop(self.device, 'zoned') != 'off':
            if zfs_setprop(self.device, 'zoned', 'off'):
                raise ex.excError
        if zfs_getprop(self.device, 'mountpoint') != self.mount_point:
            if not zfs_setprop(self.device, 'mountpoint', self.mount_point):
                raise ex.excError

        try:
            os.unlink(self.mount_point+"/.opensvc")
        except:
            pass
        ret, out, err = self.vcall([rcEnv.syspaths.zfs, 'mount', self.device])
        if ret != 0:
            ret, out, err = self.vcall([rcEnv.syspaths.zfs, 'mount', '-O', self.device])
            if ret != 0:
                raise ex.excError

    def mount_generic(self):
        if self.fs_type != "":
            fstype = ['-t', self.fs_type]
        else:
            fstype = []

        if self.mount_options != "":
            mntopt = ['-o', self.mount_options]
        else:
            mntopt = []

        if self.loopdevice is None:
            device = self.device
        else:
            device = self.loopdevice

        cmd = ['mount'] + fstype + mntopt + [device, self.mount_point]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def kill_users(self):
        import glob
        for path in glob.glob("/proc/*/fd/*") + glob.glob("/proc/*/cwd") + glob.glob("/proc/*/exe"):
            try:
                dest = os.path.realpath(path)
            except:
                continue
            if dest.startswith(self.mount_point):
                elements = path.split("/")
                try:
                    pid = int(elements[2])
                except:
                    continue
                try:
                    with open("/proc/%d/cmdline" % pid, "r") as ofile:
                        cmdline = ofile.read()
                except Exception as exc:
                    self.log.warning(str(exc))
                    cmdline = ""
                self.log.info("kill -9 %d (cmdline: %s)", pid, cmdline)
                os.kill(pid, 9)

    def stop(self):
        if self.mounts is None:
            self.mounts = rcMounts.Mounts()
        if self.is_up() is False:
            self.log.info("%s is already umounted", self.label)
            return
        if not os.path.exists(self.mount_point):
            raise ex.excError('mount point %s does not exist' % self.mount_point)
        try:
            os.stat(self.mount_point)
        except OSError as exc:
            if exc.errno == (5, 13):
                self.log.warning("I/O error on mount point. try to umount anyway")
                self.kill_users()
            else:
                raise ex.excError(str(exc))
        self.remove_holders()
        self.remove_deeper_mounts()
        for _ in range(3):
            ret = self.try_umount()
            if ret == 0:
                break
        if ret != 0:
            raise ex.excError('failed to umount %s'%self.mount_point)
        self.mounts = None

    def remove_dev_holders(self, devpath, tree):
        dev = tree.get_dev_by_devpath(devpath)
        if dev is None:
            return
        holders_devpaths = set()
        holder_devs = dev.get_children_bottom_up()
        for holder_dev in holder_devs:
            holders_devpaths |= set(holder_dev.devpath)
        holders_devpaths -= set(dev.devpath)
        holders_handled_by_resources = self.svc.exposed_devs() & holders_devpaths
        if len(holders_handled_by_resources) > 0:
            raise ex.excError("resource %s has holders handled by other "
                              "resources: %s" % (self.rid, ", ".join(holders_handled_by_resources)))
        for holder_dev in holder_devs:
            holder_dev.remove(self)

    def remove_holders(self):
        import glob
        tree = self.svc.node.devtree
        dev_realpath = os.path.realpath(self.device)
        self.remove_dev_holders(dev_realpath, tree)

    def remove_deeper_mounts(self):
        mounts = rcMounts.Mounts()
        mnt_realpath = os.path.realpath(self.mount_point)
        for mount in mounts:
            _mnt_realpath = os.path.realpath(mount.mnt)
            if _mnt_realpath != mnt_realpath and \
               _mnt_realpath.startswith(mnt_realpath+"/"):
                ret = self.try_umount(_mnt_realpath)
                if ret != 0:
                    break

0707010005c9d3000081b40000000000000000000000015ab2175400000f72000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/resFsOSF1.py   import os
import rcMountsOSF1 as rcMounts
import resFs as Res
from rcUtilities import qcall, protected_mount, getmount
from rcGlobalEnv import rcEnv
import rcExceptions as ex
from stat import *

def try_umount(self):
    cmd = ['umount', self.mount_point]
    (ret, out, err) = self.vcall(cmd, err_to_warn=True)
    if ret == 0:
        return 0

    if "not currently mounted" in err:
        return 0

    """ don't try to kill process using the source of a
        protected bind mount
    """
    if protected_mount(self.mount_point):
        return 1

    """ best effort kill of all processes that might block
        the umount operation. The priority is given to mass
        action reliability, ie don't contest oprator's will
    """
    cmd = ['sync']
    (ret, out, err) = self.vcall(cmd)

    for i in range(4):
        cmd = ['fuser', '-kcv', self.mount_point]
        (ret, out, err) = self.vcall(cmd, err_to_info=True)
        self.log.info('umount %s'%self.mount_point)
        cmd = ['umount', self.mount_point]
        ret = qcall(cmd)
        if ret == 0:
            break

    return ret


class Mount(Res.Mount):
    def __init__(self, **kwargs):
        self.Mounts = None
        Res.Mount.__init__(self, **kwargs)

    def set_fsck_h(self):
        self.fsck_h = {
            'ufs': {
                'bin': 'fsck',
                'cmd': ['fsck', '-p', self.device], 'allowed_ret': []
            },
        }

    def is_up(self):
        self.Mounts = rcMounts.Mounts()
        ret = self.Mounts.has_mount(self.device, self.mount_point)
        if ret:
            return True

        if self.fs_type not in ["advfs"] + self.netfs:
            # might be a loopback mount
            try:
                mode = os.stat(self.device)[ST_MODE]
            except:
                self.log.debug("can not stat %s" % self.device)
                return False

        return False

    def sub_devs(self):
        if '#' in self.device:
            dom, fset = self.device.split('#')
            for r in self.svc.get_resources('disk.vg'):
                if r.name == dom:
                    # no need to compute device list: the vg resource will do the job
                    return set()
            import rcAdvfs
            try:
                o = rcAdvfs.Fdmns()
                d = o.get_fdmn(dom)
            except rcAdvfs.ExInit as e:
                return set()
            if d is None:
                return set()
            return set(d.list_volnames())
        else:
            return set([self.device])

    def can_check_writable(self):
        return True

    def start(self):
        if self.Mounts is None:
            self.Mounts = rcMounts.Mounts()
        Res.Mount.start(self)

        if self.is_up() is True:
            self.log.info("%s is already mounted" % self.label)
            return 0

        self.fsck()
        if not os.path.exists(self.mount_point):
            os.makedirs(self.mount_point, 0o755)
        if self.fs_type != "":
            fstype = ['-t', self.fs_type]
        else:
            fstype = []
        if self.mount_options != "":
            mntopt = ['-o', self.mount_options]
        else:
            mntopt = []
        cmd = ['mount']+fstype+mntopt+[self.device, self.mount_point]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError
        self.Mounts = None
        self.can_rollback = True

    def stop(self):
        if self.Mounts is None:
            self.Mounts = rcMounts.Mounts()
        if self.is_up() is False:
            self.log.info("%s is already umounted" % self.label)
            return
        for i in range(3):
            ret = try_umount(self)
            if ret == 0: break
        if ret != 0:
            self.log.error('failed to umount %s'%self.mount_point)
            raise ex.excError
        self.Mounts = None

if __name__ == "__main__":
    for c in (Mount,) :
        help(c)

  0707010005c9d4000081b40000000000000000000000015ab2175400001807000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/resFsSunOS.py  import os
import time
import re

import rcStatus
import rcMountsSunOS as rcMounts
import resFs as Res
import rcExceptions as ex
from rcZfs import zfs_getprop, zfs_setprop
from rcUtilities import justcall
from rcGlobalEnv import rcEnv

class Mount(Res.Mount):
    """
    SunOS fs resource driver.
    """
    def __init__(self,
                 rid,
                 mount_point,
                 device,
                 fs_type,
                 mount_options,
                 snap_size=None,
                 **kwargs):
        self.rdevice = device.replace('/dsk/','/rdsk/',1)
        self.Mounts = rcMounts.Mounts()
        Res.Mount.__init__(self,
                           rid=rid,
                           mount_point=mount_point,
                           device=device,
                           fs_type=fs_type,
                           mount_options=mount_options,
                           snap_size=snap_size,
                           **kwargs)

    def set_fsck_h(self):
        self.fsck_h = {
            'ufs': {
                'bin': 'fsck',
                'cmd':       ['fsck', '-F', 'ufs', '-y', self.rdevice],
                'reportcmd': ['fsck', '-F', 'ufs', '-m', self.rdevice],
                'reportclean': [ 32 ],
            },
            'vxfs': {
                'bin': 'fsck',
                'cmd':       ['fsck', '-F', 'vxfs', '-y', self.rdevice],
                'reportcmd': ['fsck', '-F', 'vxfs', '-m', self.rdevice],
                'reportclean': [ 32 ],
            },
        }

    def is_up(self):
        self.Mounts = rcMounts.Mounts()
        return self.Mounts.has_mount(self.device, self.mount_point)

    def start(self):
        self.Mounts = None
        Res.Mount.start(self)
        m = re.match("<(\w+)>", self.mount_point)
        if m:
            # the zone was not created when the service was built. now it should,
            # so try the redetect the zonepath
            zone = m.group(1)
            for r in self.svc.get_resources("container.zone"):
                if r.name == zone:
                    zonepath = r.get_zonepath()
                    self.mount_point = re.sub("<\w+>", zonepath, self.mount_point)

        if self.fs_type == 'zfs' :
            if 'noaction' not in self.tags and zfs_getprop(self.device, 'canmount' ) != 'noauto' :
                self.log.info("%s should be set to canmount=noauto (zfs set canmount=noauto %s)"%(self.label, self.device))

        if self.is_up() is True:
            self.log.info("%s is already mounted" % self.label)
            return

        if self.fs_type == 'zfs' :
            if not self.encap and not self.svc.config.has_option(self.rid, 'zone') and zfs_getprop(self.device, 'zoned') != 'off':
                if zfs_setprop(self.device, 'zoned', 'off'):
                    raise ex.excError
            if zfs_getprop(self.device, 'mountpoint') != self.mount_point:
                if not zfs_setprop(self.device, 'mountpoint', self.mount_point):
                    raise ex.excError

            self.Mounts = None
            if self.is_up() is True:
                return

            (stdout,stderr,returncode)= justcall(['rm', self.mount_point+"/.opensvc" ])
            ret, out, err = self.vcall([rcEnv.syspaths.zfs, 'mount', self.device ])
            if ret != 0:
                ret, out, err = self.vcall([rcEnv.syspaths.zfs, 'mount', '-O', self.device ])
                if ret != 0:
                    raise ex.excError
            return
        elif self.fs_type != "":
            fstype = ['-F', self.fs_type]
            self.fsck()
        else:
            fstype = []

        if self.mount_options != "":
            mntopt = ['-o', self.mount_options]
        else:
            mntopt = []

        if not os.path.exists(self.mount_point):
            os.makedirs(self.mount_point, 0o755)

        for i in range(3):
            ret = self.try_mount(fstype, mntopt)
            if ret == 0: break
            time.sleep(1)

        self.Mounts = None

        if ret != 0:
            raise ex.excError

        self.can_rollback = True

    def can_check_writable(self):
        if self.fs_type != 'zfs':
            return True
        pool = self.device.split("/")[0]
        cmd = [rcEnv.syspaths.zpool, "status", pool]
        out, err, ret = justcall(cmd)
        if "state: SUSPENDED" in out:
            self.status_log("pool %s is suspended")
            return False
        return True

    def try_mount(self, fstype, mntopt):
        cmd = ['mount'] + fstype + mntopt + [self.device, self.mount_point]
        ret, out, err = self.vcall(cmd)
        return ret

    def try_umount(self):
        if self.fs_type == 'zfs' :
            ret, out, err = self.vcall(['zfs', 'umount', self.device ], err_to_info=True)
            if ret != 0 :
                ret, out, err = self.vcall(['zfs', 'umount', '-f', self.device ], err_to_info=True)
                if ret != 0 :
                    raise ex.excError
            return
        (ret, out, err) = self.vcall(['umount', self.mount_point], err_to_info=True)
        if ret == 0 :
            return
        for i in range(4):
            (ret, out, err) = self.vcall(['fuser', '-ck', self.mount_point],
                                    err_to_info=True)
            (ret, out, err) = self.vcall(['umount', self.mount_point],
                                    err_to_info=True)
            if ret == 0 :
                return
            if self.fs_type != 'lofs' :
                (ret, out, err) = self.vcall(['umount', '-f', self.mount_point],
                                        err_to_info=True)
                if ret == 0 :
                    return
        raise ex.excError

    def stop(self):
        self.Mounts = None
        if self.is_up() is False:
            self.log.info("%s is already umounted" % self.label)
            return

        try:
            self.try_umount()
        except:
            self.Mounts = None
            self.log.error("failed")
            raise ex.excError
        self.Mounts = None

if __name__ == "__main__":
    for c in (Mount,) :
        help(c)

 0707010005c9d5000081b40000000000000000000000015ab21754000011dd000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/resFsWindows.py    #
# Copyright (c) 2014 Arnaud Veron <arnaud.veron@opensvc.com>
# Copyright (c) 2014 Christophe Varoqui <christophe.varoqui@opensvc.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# To change this template, choose Tools | Templates
# and open the template in the editor.

import os

import rcStatus
from rcGlobalEnv import rcEnv
rcMounts = __import__('rcMounts'+rcEnv.sysname)
import resFs as Res
import rcExceptions as ex

def diskpartfile_name(self):
    return os.path.join(rcEnv.pathvar, 'diskpart_' + self.svc.svcname)

def online_drive(self, driveindex):
    diskpart_file = diskpartfile_name(self) + '_online_disk_' + str(driveindex)
    with open(diskpart_file, 'w') as f:
        f.write("select disk=%s\n"%driveindex)
        f.write("online disk\n")
        f.write("exit\n")
    self.log.info("bring disk %s online"%driveindex)
    cmd = ['diskpart', '/s', diskpart_file]
    (ret, out, err) = self.vcall(cmd)
    if ret != 0:
        raise ex.excError("Failed to run command %s"% ' '.join(cmd) )

def offline_drive(self, driveindex):
    diskpart_file = diskpartfile_name(self) + '_offline_disk_' + str(driveindex)
    with open(diskpart_file, 'w') as f:
        f.write("select disk=%s\n"%driveindex)
        f.write("offline disk\n")
        f.write("exit\n")
    self.log.info("bring disk %s offline"%driveindex)
    cmd = ['diskpart', '/s', diskpart_file]
    (ret, out, err) = self.vcall(cmd)
    if ret != 0:
        raise ex.excError("Failed to run command %s"% ' '.join(cmd) )

def mount(self):
    v = self.get_volume()
    letter = self.mountpt_to_driverletter()
    ret = 0
    self.log.info("assign drive letter %s to volume %s"%(letter, self.device))
    v.DriveLetter = letter
    if not v.Automount:
        self.log.info("mount volume %s"%self.device)
        ret, = v.Mount()
    v = self.get_volume(refresh=True)
    return ret

def try_umount(self):
    v = self.get_volume()
    letter = self.mountpt_to_driverletter()
    self.log.info("unassign drive letter %s from volume %s"%(letter, self.device))
    v.DriveLetter = None
    v = self.get_volume(refresh=True)
    if v.DriveLetter is None:
        return 0
    return 1

class Mount(Res.Mount):
    """ define Windows mount/umount doAction """

    def on_add(self):
        if hasattr(self.svc, 'wmi'):
            return
        import wmi
        self.svc.wmi = wmi.WMI()

    def mountpt_to_driverletter(self):
        i = self.mountPoint.index(':')
        return self.mountPoint[:i+1]

    def get_volume(self, refresh=False):
        if not refresh and hasattr(self, "volume"):
            return self.volume
        l = self.svc.wmi.Win32_Volume()
        for v in l:
	    print("DBG devid %s " % v.DeviceId)
            if v.DeviceId == self.device:
                # cache result
                self.volume = v
                return v
        raise ex.excError("Volume %s not found" % self.device)

    # a completer
    def match_mount(self, i, dev, mnt):
        return True

    # a completer
    def is_online(self, dev):
        return True

    def is_up(self):
        return rcMounts.Mounts(wmi=self.svc.wmi).has_mount(self.device, self.mountPoint)

    def start(self):
	if self.is_online(self.device) is True:
	    self.log.info("fs(%s %s) is already online"%(self.device, self.mountPoint))
        if self.is_up() is True:
            self.log.info("fs(%s %s) is already mounted"%(self.device, self.mountPoint))
            return 0
	ret = mount(self)
	if ret != 0:
	    return 1
        self.can_rollback = True
	return 0

    def stop(self):
        if self.is_up() is False:
            self.log.info("fs(%s %s) is already umounted"%
                    (self.device, self.mountPoint))
            return 0
        ret = try_umount(self)
        if ret != 0:
            raise ex.excError('failed to dismount %s'%self.mountPoint)
        return 0

if __name__ == "__main__":
    for c in (Mount,) :
        help(c)
   0707010005c9d6000081b40000000000000000000000015ab2175400005047000000660000000000000000000000000000002400000000root/usr/share/opensvc/lib/resIp.py   """
Generic ip resource driver.
"""
from __future__ import unicode_literals

import os
import re

import resources as Res
from rcGlobalEnv import rcEnv
from rcUtilities import qcall, which, getaddr, lazy
from converters import convert_duration
import rcStatus
import rcExceptions as ex

IFCONFIG_MOD = __import__('rcIfconfig'+rcEnv.sysname)

class Ip(Res.Resource):
    """
    Base ip resource driver.
    """

    def __init__(self,
                 rid=None,
                 ipdev=None,
                 ipname=None,
                 mask=None,
                 gateway=None,
                 type="ip",
                 expose=None,
                 **kwargs):
        Res.Resource.__init__(self, rid, type=type, **kwargs)
        self.ipdev = ipdev
        self.ipname = ipname
        self.mask = mask
        self.gateway = gateway
        self.set_label()
        self.lockfd = None
        self.stacked_dev = None
        self.addr = None
        self.expose = expose

    @lazy
    def dns_name_suffix(self):
        try:
            dns_name_suffix = self.svc.conf_get(self.rid, "dns_name_suffix").strip("'\"$#")
        except ex.OptNotFound:
            dns_name_suffix = None
        return dns_name_suffix

    def set_label(self):
        """
        Set the resource label property.
        """
        self.label = str(self.ipname) + '@' + self.ipdev

    def status_info(self):
        """
        Contribute resource key/val pairs to the resource info.
        """
        if self.ipname is None:
            return {}
        try:
            self.getaddr()
        except ex.excError:
            pass
        data = {
            "ipaddr": self.addr,
            "ipdev": self.ipdev,
        }
        if self.gateway:
            data["gateway"] = self.gateway
        if self.mask is not None:
            from rcUtilities import to_cidr
            data["mask"] = to_cidr(self.mask)
        if self.expose:
            data["expose"] = self.expose
        return data

    def _info(self):
        """
        Contribute resource key/val pairs to the service's resinfo.
        """
        if self.ipname is None:
            return
        try:
            self.getaddr()
        except ex.excError:
            pass
        from rcUtilities import to_cidr
        data = [
            ["ipaddr", self.addr],
            ["ipname", self.ipname],
            ["ipdev", self.ipdev],
            ["gateway", str(self.gateway)],
        ]
        if self.mask is not None:
            data.append(["mask", str(to_cidr(self.mask))])
        return data

    def getaddr(self, cache_fallback=False):
        """
        Try resolving the ipname into an ip address. If the resolving fails and
        <cache_fallback> is True, use the last successful resolution result.
        """
        if self.ipname is None:
            raise ex.excError("ip address is not allocated yet")
        if self.addr is not None:
            return
        try:
            self.log.debug("resolving %s", self.ipname)
            self.addr = getaddr(self.ipname, cache_fallback=cache_fallback, log=self.log)
        except Exception as exc:
            if not self.is_disabled():
                raise ex.excError("could not resolve name %s: %s" % (self.ipname, str(exc)))

    def __str__(self):
        return "%s ipdev=%s ipname=%s" % (Res.Resource.__str__(self),\
                                         self.ipdev, self.ipname)
    def setup_environ(self):
        """
        Set the main resource properties as environment variables, so they
        are available to triggers.
        """
        os.environ['OPENSVC_IPDEV'] = str(self.ipdev)
        os.environ['OPENSVC_IPNAME'] = str(self.ipname)
        os.environ['OPENSVC_MASK'] = str(self.mask)
        try:
            self.getaddr()
            os.environ['OPENSVC_IPADDR'] = str(self.addr)
        except:
            pass
        elements = self.rid.split('#')
        if len(elements) == 2:
            index = elements[1]
        else:
            index = ''
        var = 'OPENSVC_IP'+index
        vals = []
        for prop in ['ipname', 'ipdev', 'addr', 'mask']:
            if getattr(self, prop) is not None:
                vals.append(str(getattr(self, prop)))
            else:
                vals.append('unknown')
        val = ' '.join(vals)
        os.environ[var] = val

    def _status(self, verbose=False):
        """
        Evaluate the ip resource status.
        """
        try:
            self.getaddr()
        except Exception as exc:
            self.status_log(str(exc))
            if "not allocated" in str(exc):
                return rcStatus.DOWN
            else:
                return rcStatus.WARN
        ifconfig = IFCONFIG_MOD.ifconfig()
        intf = ifconfig.interface(self.ipdev)
        if intf is None and "dedicated" not in self.tags:
            self.status_log("interface %s not found" % self.ipdev)
            return rcStatus.DOWN
        try:
            if self.is_up():
                return rcStatus.UP
            else:
                return rcStatus.DOWN
        except ex.excNotSupported:
            self.status_log("not supported", "info")
            return rcStatus.NA
        except ex.excError as exc:
            self.status_log(str(exc), "error")
            return rcStatus.WARN

    def arp_announce(self):
        """
        Announce to neighbors the ip address is plumbed on ipdev through a
        arping broadcast of unsollicited packets.
        """
        if ':' in self.addr:
            return
        if not which("arping"):
            self.log.warning("arp announce skipped. install 'arping'")
            return
        cmd = ["arping", "-U", "-c", "1", "-I", self.ipdev, "-s", self.addr, self.addr]
        self.log.info(' '.join(cmd))
        qcall(cmd)

    def abort_start(self):
        """
        Return True if the service start should be aborted because of a routed
        ip conflict.
        """
        if 'nonrouted' in self.tags or 'noaction' in self.tags:
            return False
        if self.addr is None:
            return False
        if not self.is_up() and self.check_ping():
            return True
        return False

    def start_link(self):
        """
        Start the ipdev link.
        """
        raise ex.MissImpl('start_link')

    def check_ping(self, count=1, timeout=5):
        """
        Test if the ip is seen as active on the newtorks.
        """
        raise ex.MissImpl('check_ping')

    def startip_cmd(self):
        """
        The os/driver specific start implementation.
        """
        raise ex.MissImpl('startip_cmd')

    def stopip_cmd(self):
        """
        The os/driver specific stop implementation.
        """
        raise ex.MissImpl('stopip_cmd')

    def is_up(self):
        """
        Return True if the ip is plumbed.
        """
        ifconfig = self.get_ifconfig()
        if ifconfig.has_param("ipaddr", self.addr) is not None or \
           ifconfig.has_param("ip6addr", self.addr) is not None:
            self.log.debug("%s@%s is up", self.addr, self.ipdev)
            return True
        self.log.debug("%s@%s is down", self.addr, self.ipdev)
        return False

    def allow_start(self):
        """
        Do sanity checks before allowing the start.
        """
        if self.is_up() is True:
            self.log.info("%s is already up on %s", self.addr, self.ipdev)
            raise ex.IpAlreadyUp(self.addr)
        ifconfig = IFCONFIG_MOD.ifconfig()
        intf = ifconfig.interface(self.ipdev)
        if intf is None:
            self.log.error("interface %s not found. Cannot stack over it.", self.ipdev)
            raise ex.IpDevDown(self.ipdev)
        if not intf.flag_up:
            if hasattr(intf, 'groupname') and intf.groupname != "":
                l = [_intf for _intf in ifconfig.get_matching_interfaces('groupname', intf.groupname) if _intf.flag_up]
                if len(l) == 1:
                    self.log.info("switch %s to valid alternate path %s", self.ipdev, l[0].name)
                    intf = l[0]
                    self.ipdev = l[0].name
            try:
                self.start_link()
            except ex.MissImpl:
                self.log.error("interface %s is not up. Cannot stack over it.", self.ipdev)
                raise ex.IpDevDown(self.ipdev)
        if not self.svc.abort_start_done and self.check_ping():
            self.log.error("%s is already up on another host", self.addr)
            raise ex.IpConflict(self.addr)
        return

    def lock(self):
        """
        Acquire the startip lock, protecting against allocation of the same
        ipdev stacked device to multiple resources or multiple services.
        """
        import lock
        timeout = convert_duration(self.svc.options.waitlock)
        if timeout < 0:
            timeout = 120
        delay = 1
        lockfd = None
        action = "startip"
        lockfile = os.path.join(rcEnv.paths.pathlock, action)
        details = "(timeout %d, delay %d, action %s, lockfile %s)" % \
                  (timeout, delay, action, lockfile)
        self.log.debug("acquire startip lock %s", details)

        try:
            lockfd = lock.lock(timeout=timeout, delay=delay, lockfile=lockfile, intent="startip")
        except lock.LockTimeout as exc:
            raise ex.excError("timed out waiting for lock %s: %s" % (details, str(exc)))
        except lock.LockNoLockFile:
            raise ex.excError("lock_nowait: set the 'lockfile' param %s" % details)
        except lock.LockCreateError:
            raise ex.excError("can not create lock file %s" % details)
        except lock.LockAcquire as exc:
            raise ex.excError("another action is currently running %s: %s" % (details, str(exc)))
        except ex.excSignal:
            raise ex.excError("interrupted by signal %s" % details)
        except Exception as exc:
            self.save_exc()
            raise ex.excError("unexpected locking error %s: %s" % (details, str(exc)))

        if lockfd is not None:
            self.lockfd = lockfd

    def unlock(self):
        """
        Release the startip lock.
        """
        import lock
        lock.unlock(self.lockfd)

    @staticmethod
    def get_ifconfig():
        """
        Wrapper around the os specific rcIfconfig module's ifconfig function.
        Return a parsed ifconfig dataset.
        """
        return IFCONFIG_MOD.ifconfig()

    def start(self):
        """
        Start the resource.
        """
        if self.ipname is None:
            self.log.warning("skip start: no ipname set")
            return
        self.getaddr()
        try:
            self.allow_start()
        except (ex.IpConflict, ex.IpDevDown):
            raise ex.excError
        except (ex.IpAlreadyUp, ex.IpNoActions):
            return
        self.log.debug('pre-checks passed')

        self.lock()
        try:
            arp_announce = self.start_locked()
        finally:
            self.unlock()

        if arp_announce:
            self.arp_announce()

        try:
            self.dns_update()
        except ex.excError as exc:
            self.log.error(str(exc))

    def start_locked(self):
        """
        The start codepath fragment protected by the startip lock.
        """
        ifconfig = self.get_ifconfig()
        if self.mask is None:
            intf = ifconfig.interface(self.ipdev)
            if intf is None:
                raise ex.excError("netmask parameter is mandatory with 'noalias' tag")
            self.mask = intf.mask
        if self.mask == '':
            raise ex.excError("No netmask set on parent interface %s" % self.ipdev)
        elif isinstance(self.mask, list):
            if len(self.mask) > 0:
                self.mask = self.mask[0]
            else:
                raise ex.excError("No netmask set on parent interface %s" % self.ipdev)
        if 'noalias' in self.tags:
            self.stacked_dev = self.ipdev
        else:
            self.stacked_dev = ifconfig.get_stacked_dev(self.ipdev,\
                                                        self.addr,\
                                                        self.log)
        if self.stacked_dev is None:
            raise ex.excError("could not determine a stacked dev for parent "
                              "interface %s" % self.ipdev)

        arp_announce = True
        try:
            ret = self.startip_cmd()[0]
            self.can_rollback = True
        except ex.excNotSupported:
            self.log.info("start ip not supported")
            ret = 0
            arp_announce = False

        if ret != 0:
            raise ex.excError("failed")

        return arp_announce

    def dns_update(self):
        """
        Post a dns update request to the collector.
        """
        if rcEnv.dbopensvc is None:
            return

        if self.ipname is None:
            self.log.debug("skip dns update: ipname is not set")
            return

        try:
            self.svc.conf_get(self.rid, "dns_update")
        except ex.OptNotFound:
            self.log.debug("skip dns update: dns_update is not set")
            return

        if not self.is_up():
            self.log.debug("skip dns update: resource is not up")
            return

        if self.dns_name_suffix is None:
            self.log.debug("dns update: dns_name_suffix is not set")

        try:
            self.getaddr()
        except ex.excError as exc:
            self.log.error(str(exc))
            return

        post_data = {
            "content": self.addr,
        }

        if self.dns_name_suffix:
            post_data["name"] = self.dns_name_suffix

        try:
            data = self.svc.node.collector_rest_post(
                "/dns/services/records",
                post_data,
                svcname=self.svc.svcname,
            )
        except Exception as exc:
            raise ex.excError("dns update failed: "+str(exc))
        if "error" in data:
            raise ex.excError(data["error"])

        self.log.info("dns updated")


    def stop(self):
        """
        Stop the resource.
        """
        if self.ipname is None:
            self.log.warning("skip stop: no ipname set")
            return
        self.getaddr(cache_fallback=True)
        if self.is_up() is False:
            self.log.info("%s is already down on %s", self.addr, self.ipdev)
            return
        ifconfig = self.get_ifconfig()
        if 'noalias' in self.tags:
            self.stacked_dev = self.ipdev
        else:
            self.stacked_dev = ifconfig.get_stacked_dev(self.ipdev,\
                                                        self.addr,\
                                                        self.log)
        if self.stacked_dev is None:
            raise ex.excError

        try:
            ret = self.stopip_cmd()[0]
        except ex.excNotSupported:
            self.log.info("stop ip not supported")
            return

        if ret != 0:
            self.log.error("failed")
            raise ex.excError

        import time
        tmo = 15
        idx = 0
        for idx in range(tmo):
            if not self.check_ping(count=1, timeout=1):
                break
            time.sleep(1)

        if idx == tmo-1:
            self.log.error("%s refuse to go down", self.addr)
            raise ex.excError

    def allocate(self):
        """
        Request an ip in the ipdev network from the collector.
        """
        if rcEnv.dbopensvc is None:
            return

        import ipaddress

        try:
            self.conf_get("ipname")
            self.log.info("skip allocate: an ip is already defined")
            return
        except ex.RequiredOptNotFound:
            pass
        except ex.OptNotFound:
            pass

        if self.ipdev is None:
            self.log.info("skip allocate: ipdev is not set")
            return

        try:
            # explicit network setting
            network = self.svc.conf_get(self.rid, "network")
        except ex.OptNotFound:
            network = None

        if network is None:
            # implicit network: the network of the first ipdev ip
            ifconfig = IFCONFIG_MOD.ifconfig()
            intf = ifconfig.interface(self.ipdev)
            try:
                if isinstance(intf.ipaddr, list):
                    baseaddr = intf.ipaddr[0]
                else:
                    baseaddr = intf.ipaddr
                network = str(ipaddress.IPv4Interface(baseaddr).network.network_address)
            except (ValueError, IndexError):
                self.log.info("skip allocate: ipdev %s has no configured address "
                              "and network is not set", self.ipdev)
                return

        post_data = {
            "network": network,
        }

        if self.dns_name_suffix:
            post_data["name"] = self.dns_name_suffix
        else:
            self.log.debug("allocate: dns_name_suffix is not set")

        try:
            data = self.svc.node.collector_rest_post(
                "/networks/%s/allocate" % network,
                post_data,
                svcname=self.svc.svcname,
            )
        except Exception as exc:
            raise ex.excError("ip allocation failed: "+str(exc))
        if "error" in data:
            raise ex.excError(data["error"])

        if "info" in data:
            self.log.info(data["info"])

        self.ipname = data["data"]["ip"]
        self.addr = self.ipname
        self.set_label()
        self.svc._set(self.rid, "ipname", self.ipname)
        if self.gateway in (None, ""):
            gateway = data.get("data", {}).get("network", {}).get("gateway")
            if gateway:
                self.log.info("set gateway=%s", gateway)
                self.svc._set(self.rid, "gateway", gateway)
                self.gateway = gateway
        if self.mask in (None, ""):
            netmask = data.get("data", {}).get("network", {}).get("netmask")
            if netmask:
                self.log.info("set netmask=%s", netmask)
                self.svc._set(self.rid, "netmask", netmask)
                self.mask = str(netmask)
        self.log.info("ip %s allocated", self.ipname)

    def release(self):
        """
        Release an allocated ip a collector managed network.
        """
        if rcEnv.dbopensvc is None:
            return

        if self.ipname is None:
            self.log.info("skip release: no ipname set")
            return

        try:
            self.getaddr()
        except ex.excError:
            self.log.info("skip release: ipname does not resolve to an address")
            return

        post_data = {}

        if self.dns_name_suffix:
            post_data["name"] = self.dns_name_suffix
        else:
            self.log.debug("allocate: dns_name_suffix is not set")

        try:
            data = self.svc.node.collector_rest_post(
                "/networks/%s/release" % self.addr,
                post_data,
                svcname=self.svc.svcname,
            )
        except Exception as exc:
            raise ex.excError("ip release failed: "+str(exc))
        if "error" in data:
            self.log.warning(data["error"])
            return

        if "info" in data:
            self.log.info(data["info"])

        self.svc._unset(self.rid, "ipname")
        self.log.info("ip %s released", self.ipname)

    def expose_data(self):
        if self.expose is None:
            return []
        data = []
        for expose in self.expose:
            data.append(self.parse_expose(expose))
        return data

    def parse_expose(self, expose):
        data = {}
        words = expose.split(":")
        if len(words) == 2:
            try:
                data["host_port"] = int(words[1])
            except ValueError:
                raise ex.excError("invalid host port format %s. expected integer" % words[1])
        words = re.split("[-/]", words[0])
        if len(words) != 2:
            raise ex.excError("invalid expose format %s. expected <nsport>/<proto>[:<hostport>]" % expose)
        try:
            data["port"] = int(words[0])
        except ValueError:
            raise ex.excError("invalid expose port format %s. expected integer" % words[0])
        if words[1] not in ("tcp", "udp"):
            raise ex.excError("invalid expose protocol %s. expected tcp or udp" % words[1])
        data["protocol"] = words[1]
        return data



 0707010005c9d7000081b40000000000000000000000015ab2175400000388000000660000000000000000000000000000002700000000root/usr/share/opensvc/lib/resIpAIX.py    import resIp as Res
import rcExceptions as ex
from rcUtilitiesAIX import check_ping
from rcUtilities import to_cidr, to_dotted

class Ip(Res.Ip):
    def check_ping(self, count=1, timeout=5):
        self.log.info("checking %s availability"%self.addr)
        return check_ping(self.addr, count=count, timeout=timeout)

    def arp_announce(self):
        return

    def startip_cmd(self):
        if ':' in self.addr:
            cmd = ['ifconfig', self.ipdev, 'inet6', 'alias', '/'.join([self.addr, to_cidr(self.mask)])]
        else:
            cmd = ['ifconfig', self.ipdev, self.addr, 'netmask', to_dotted(self.mask), 'alias']
        return self.vcall(cmd)

    def stopip_cmd(self):
        if ':' in self.addr:
            cmd = ['ifconfig', self.ipdev, 'inet6', self.addr, 'delete']
        else:
            cmd = ['ifconfig', self.ipdev, self.addr, 'delete']
        return self.vcall(cmd)

0707010005c9d8000081b40000000000000000000000015ab21754000010b3000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/resIpAmazon.py import resIp
import os
import rcStatus
from rcGlobalEnv import rcEnv
import rcExceptions as ex
from rcAmazon import Amazon
from rcUtilities import getaddr

rcIfconfig = __import__('rcIfconfig'+rcEnv.sysname)

class Ip(resIp.Ip, Amazon):
    def __init__(self,
                 rid=None,
                 ipname=None,
                 ipdev=None,
                 eip=None,
                 **kwargs):
        resIp.Ip.__init__(self,
                          rid=rid,
                          ipname=ipname,
                          ipdev=ipdev,
                          **kwargs)
        self.label = "ec2 ip %s@%s" % (ipname, ipdev)
        if eip:
            self.label += ", eip %s" % eip

        self.eip = eip

    def get_eip(self):
        ip = getaddr(self.eip, True)
        data = self.aws(["ec2", "describe-addresses", "--public-ips", self.eip], verbose=False)
        try:
            addr = data["Addresses"][0]
        except:
            addr = None
        return addr

    def get_instance_private_addresses(self):
        instance_data = self.get_instance_data(refresh=True)
        if instance_data is None:
            raise ex.excError("can't find instance data")

        ips = []
        for eni in instance_data["NetworkInterfaces"]:
            ips += [ pa["PrivateIpAddress"] for pa in eni["PrivateIpAddresses"] ]
        return ips

    def get_network_interface(self):
        ifconfig = rcIfconfig.ifconfig()
        intf = ifconfig.interface(self.ipdev)
        ips = set(intf.ipaddr + intf.ip6addr)
        instance_data = self.get_instance_data(refresh=True)
        if instance_data is None:
            raise ex.excError("can't find instance data")

        for eni in instance_data["NetworkInterfaces"]:
            _ips = set([ pa["PrivateIpAddress"] for pa in eni["PrivateIpAddresses"] ])
            if len(ips & _ips) > 0:
                return eni["NetworkInterfaceId"]

    def is_up(self):
        """Returns True if ip is associated with this node
        """
        self.getaddr()
        ips = self.get_instance_private_addresses()
        if self.addr not in ips:
            return False
        return True

    def _status(self, verbose=False):
        try:
            s = self.is_up()
        except ex.excError as e:
            self.status_log(str(e))
            return rcStatus.WARN
        if s:
            return rcStatus.UP
        else:
            return rcStatus.DOWN

    def check_ping(self, count=1, timeout=5):
        pass

    def start_assign(self):
        if self.is_up():
            self.log.info("ec2 ip %s is already assigned to this node" % self.addr)
            return
        eni = self.get_network_interface()
        if eni is None:
            raise ex.excError("could not find ec2 network interface for %s" % self.ipdev)
        data = self.aws([
         "ec2", "assign-private-ip-addresses",
         "--network-interface-id", eni,
         "--private-ip-address", self.addr,
         "--allow-reassignment"
        ])
        self.can_rollback = True

    def start_associate(self):
        if self.eip is None:
            return

        eip = self.get_eip()
        if eip is None:
            raise ex.excError("eip %s is not allocated" % self.eip)
        if "PrivateIpAddress" in eip and eip["PrivateIpAddress"] == self.addr:
            self.log.info("eip %s is already associated to private ip %s" % (eip["PublicIp"], self.addr))
            return
        data = self.aws([
          "ec2", "associate-address",
          "--allocation-id", eip["AllocationId"],
          "--private-ip-address", self.addr,
          "--instance-id", self.get_instance_id()
        ])


    def start(self):
        self.start_assign()
        self.start_associate()

    def stop(self):
        if not self.is_up():
            self.log.info("ec2 ip %s is already unassigned from this node" % self.addr)
            return
        eni = self.get_network_interface()
        if eni is None:
            raise ex.excError("could not find ec2 network interface for %s" % self.ipdev)
        data = self.aws([
         "ec2", "unassign-private-ip-addresses",
         "--network-interface-id", eni,
         "--private-ip-address", self.addr
        ])

    def shutdown(self):
        pass

 0707010005c9d9000081b40000000000000000000000015ab2175400003281000000660000000000000000000000000000002700000000root/usr/share/opensvc/lib/resIpCni.py    """
A CNI driver following the specs available at
https://github.com/containernetworking/cni/blob/master/SPEC.md
"""
import os
import hashlib
import json
from subprocess import Popen, PIPE

import resIpLinux as Res
import rcExceptions as ex
import rcIfconfigLinux as rcIfconfig
import rcStatus
from rcGlobalEnv import rcEnv
from rcUtilities import which, justcall, to_cidr, lazy, bencode, bdecode

CNI_VERSION = "0.2.0"
PORTMAP_CONF = {
    "cniVersion": CNI_VERSION,
    "name": "osvc-portmap",
    "type": "portmap",
    "snat": True,
    "capabilities": {
        "portMappings": True
    },
    "externalSetMarkChain": "OSVC-MARK-MASQ"
}

class Ip(Res.Ip):
    def __init__(self,
                 rid=None,
                 ipname=None,
                 ipdev=None,
                 network=None,
                 container_rid=None,
                 expose=None,
                 **kwargs):
        Res.Ip.__init__(self,
                        rid,
                        ipname=ipname,
                        ipdev=ipdev,
                        type="ip.cni",
                        expose=expose,
                        **kwargs)
        self.network = network
        self.container_rid = container_rid
        if container_rid:
            self.tags = self.tags | set(["docker"])
            self.tags.add(container_rid)

    def set_label(self):
        pass

    @lazy
    def label(self):
        intf = self.get_ipdev()
        label = self.network if self.network else ""
        if intf and len(intf.ipaddr) > 0:
            label += " %s/%s" % (intf.ipaddr[0], intf.mask[0])
        elif intf and len(intf.ip6addr) > 0:
            label += " %s/%s" % (intf.ip6addr[0], intf.ip6mask[0])
        if self.ipdev:
            label += "@%s" % self.ipdev
        if self.expose:
            label += " %s" % " ".join(self.expose)
        return label

    def status_info(self):
        data = Res.Ip.status_info(self)
        intf = self.get_ipdev()
        if intf and len(intf.ipaddr) > 0:
            data["ipaddr"] = intf.ipaddr[0]
        elif intf and len(intf.ip6addr) > 0:
            data["ipaddr"] = intf.ip6addr[0]
        if self.container:
            if self.container.vm_hostname != self.container.name:
                data["hostname"] = self.container.vm_hostname
            else:
                data["hostname"] = self.container.name
            if self.dns_name_suffix:
                data["hostname"] += self.dns_name_suffix
        return data

    def arp_announce(self):
        """ disable the generic arping. We do that in the guest namespace.
        """
        pass

    def get_ifconfig(self):
        if self.container_rid:
            return self.container_get_ifconfig()
        else:
            return self._get_ifconfig()

    def container_get_ifconfig(self):
        sandboxkey = self.container_sandboxkey
        if sandboxkey is None:
            return

        cmd = [rcEnv.syspaths.nsenter, "--net="+self.container_sandboxkey, "ip", "addr"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return

        ifconfig = rcIfconfig.ifconfig(ip_out=out)
        return ifconfig

    def _get_ifconfig(self):
        try:
            nspid = self.nspid
        except ex.excError as e:
            return
        if nspid is None:
            return

        cmd = [rcEnv.syspaths.ip, "netns", "exec", self.nspid, "ip", "addr"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return

        ifconfig = rcIfconfig.ifconfig(ip_out=out)
        return ifconfig

    def get_ipdev(self):
        ifconfig = self.get_ifconfig()
        if ifconfig is None:
            return
        return ifconfig.interface(self.ipdev)

    def has_ipdev(self):
        ifconfig = self.get_ifconfig()
        if ifconfig is None:
            return False
        if ifconfig.has_interface(self.ipdev) == 0:
            return False
        return True

    def has_ip(self):
        ifconfig = self.get_ifconfig()
        if ifconfig is None:
            return False
        iface = ifconfig.interface(self.ipdev)
        if iface is None:
            return False
        if len(iface.ipaddr) == 0:
            return False
        return True

    def abort_start(self):
        return False

    @lazy
    def cni_data(self):
        if not os.path.exists(self.cni_conf):
            raise ex.excError("cni configuration %s does not exist" % self.cni_conf)
        try:
            with open(self.cni_conf, "r") as ofile:
                return json.load(ofile)
        except ValueError:
            raise ex.excError("invalid json in cni configuration file %s" % self.cni_conf)

    @lazy
    def cni_plugins(self):
        try:
            return self.svc.node.conf_get("cni", "plugins")
        except ex.OptNotFound as exc:
            return exc.default

    @lazy
    def cni_config(self):
        try:
            return self.svc.node.conf_get("cni", "config")
        except ex.OptNotFound as exc:
            return exc.default

    @lazy
    def cni_portmap_conf(self):
        return os.path.join(self.cni_config, "osvc-portmap.conf")

    @lazy
    def cni_conf(self):
        return os.path.join(self.cni_config, "%s.conf" % self.network)

    def cni_bin(self, data):
        return os.path.join(self.cni_plugins, data["type"])

    @lazy
    def nspid(self):
        nspid = hashlib.sha224(self.svc.svcname).hexdigest()
        return nspid

    @lazy
    def nspidfile(self):
        return "/var/run/netns/%s" % self.nspid

    @lazy
    def container(self):
        return self.svc.resources_by_id.get(self.container_rid)

    @lazy
    def container_sandboxkey(self):
        try:
            data = self.svc.dockerlib.docker_inspect(self.container.container_id)
            return data["NetworkSettings"]["SandboxKey"]
        except (IndexError, KeyError):
            return

    @lazy
    def container_pid(self):
        try:
            data = self.svc.dockerlib.docker_inspect(self.container.container_id)
            return data["State"]["Pid"]
        except (IndexError, KeyError):
            return

    def allow_start(self):
        pass

    def start_locked(self):
        self.startip_cmd()
        return False

    def cni_cmd(self, _env, data):
        cmd = [self.cni_bin(data)]
        self.log_cmd(_env, data, cmd)
        env = {}
        env.update(rcEnv.initial_env)
        env.update(_env)
        proc = Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE, env=env)
        out, err = proc.communicate(input=bencode(json.dumps(data)))
        out = bdecode(out)
        try:
            data = json.loads(out)
        except ValueError:
            if proc.returncode == 0:
                # for example a del portmap outs nothing
                return
            raise ex.excError(err)
        if "code" in data:
            raise ex.excError(data.get("msg", ""))
        for line in out.splitlines():
            self.log.info(line)
        return data

    def log_cmd(self, _env, data, cmd):
        envs = " ".join(map(lambda x: "%s=%s" % (x[0], x[1]), _env.items()))
        text = "echo '%s' | %s %s" % (json.dumps(data), envs, " ".join(cmd))
        self.log.info(text)

    def has_netns(self):
        if self.container_rid:
            return True
        return os.path.exists(self.nspidfile)

    def add_netns(self):
        if self.container_rid:
            # the container is expected to already have a netns
            return
        if self.has_netns():
            self.log.info("netns %s already added" % self.nspid)
            return
        cmd = [rcEnv.syspaths.ip, "netns", "add", self.nspid]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError(err)

    def del_netns(self):
        if self.container_rid:
            # the container is expected janitor its netns himself
            return
        if not self.has_netns():
            self.log.info("netns %s already deleted" % self.nspid)
            return
        cmd = [rcEnv.syspaths.ip, "netns", "del", self.nspid]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError()

    def get_plugins(self):
        if "type" in self.cni_data:
            return [self.cni_data]
        elif "plugins" in self.cni_data:
            return [pdata for pdata in self.cni_data["plugins"]
                    if pdata.get("type") != "portmap"]
        raise ex.excError("no type nor plugins in cni configuration %s" % self.cni_conf)

    def runtime_config(self):
        data = []
        expose_data = self.cni_expose_data()
        for expose in expose_data:
            if "host_port" not in expose:
                continue
            data.append(expose)
        if len(data) > 0:
            return {"portMappings": data}
        return {}

    def cni_expose_data(self):
        """
        Translate opensvc expose data in the format expected by cni.
        """
        data = self.expose_data()
        _data = []
        for expose in data:
            if "host_port" not in expose:
                continue
            exdata = {
                "containerPort": expose["port"],
                "protocol": expose["protocol"],
                "hostPort": expose["host_port"],
            }
            _data.append(exdata)
        return data

    def add_cni(self):
        if not self.has_netns():
            raise ex.excError("netns %s not found" % self.nspid)
        if self.has_ipdev():
            self.log.info("cni %s already added" % self.ipdev)
            return
        _env = {
            "CNI_COMMAND": "ADD",
            "CNI_IFNAME": self.ipdev,
            "CNI_PATH": self.cni_plugins,
        }
        if self.container_rid:
            if self.container_pid is None:
                raise ex.excError("container %s is down" % self.container_rid)
            _env["CNI_CONTAINERID"] = str(self.container_pid)
            _env["CNI_NETNS"] = self.container_sandboxkey
        else:
            _env["CNI_NETNS"] = self.nspidfile

        ret = 0
        result = None
        for data in self.get_plugins():
            data["cniVersion"] = self.cni_data["cniVersion"]
            data["name"] = self.cni_data["name"]
            if result is not None:
                data["prevResult"] = result
            result = self.cni_cmd(_env, data)
        if self.expose and result:
            data = {}
            data.update(PORTMAP_CONF)
            data["runtimeConfig"] = self.runtime_config()
            data["prevResult"] = result
            result = self.cni_cmd(_env, data)

    def del_cni(self):
        if not self.has_netns():
            self.log.info("already no ip dev %s" % self.ipdev)
            return
        if not self.has_ip():
            self.log.info("already no ip on dev %s" % self.ipdev)
            return
        _env = {
            "CNI_COMMAND": "DEL",
            "CNI_IFNAME": self.ipdev,
            "CNI_PATH": self.cni_plugins,
        }
        if self.container_rid:
            if self.container_pid is None:
                self.log.info("container %s is already down" % self.container_rid)
                return
            _env["CNI_CONTAINERID"] = str(self.container_pid)
            _env["CNI_NETNS"] = self.container_sandboxkey
        else:
            _env["CNI_NETNS"] = self.nspidfile

        if self.expose:
            data = {}
            data.update(PORTMAP_CONF)
            data["runtimeConfig"] = self.runtime_config()
            result = self.cni_cmd(_env, data)
        for data in reversed(self.get_plugins()):
            data["cniVersion"] = self.cni_data["cniVersion"]
            data["name"] = self.cni_data["name"]
            result = self.cni_cmd(_env, data)

    def start(self):
        self.unset_lazy("container_pid")
        self.unset_lazy("container_sandboxkey")
        self.add_netns()
        self.add_cni()

    def stop(self):
        self.del_cni()
        self.del_netns()
        self.unset_lazy("container_pid")
        self.unset_lazy("container_sandboxkey")

    def _status(self, verbose=False):
        try:
            self.cni_data
        except ex.excError as exc:
            self.status_log(str(exc))
        _has_netns = self.has_netns()
        _has_ipdev = self.has_ipdev()
        if self.container_rid and not _has_ipdev:
            return rcStatus.DOWN
        elif not _has_netns and not _has_ipdev:
            return rcStatus.DOWN
        elif _has_netns and _has_ipdev:
            return rcStatus.UP
        else:
            if not _has_netns:
                self.status_log("netns %s not found" % self.nspid)
            if not _has_ipdev:
                self.status_log("cni %s not found" % self.ipdev)
            return rcStatus.DOWN

    def is_up(self):
        if not self.has_netns():
            return False
        if not self.has_ipdev():
            return False
        return True

   0707010005c9da000081b40000000000000000000000015ab21754000014cc000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/resIpCrossbow.py   import time
import resIpSunOS as Res
import rcExceptions as ex
from subprocess import *
from rcGlobalEnv import rcEnv
from rcUtilities import which, to_cidr, bdecode, justcall
rcIfconfig = __import__('rcIfconfig'+rcEnv.sysname)

class Ip(Res.Ip):
    def __init__(self,
                 rid=None,
                 ipdev=None,
                 ipname=None,
                 mask=None,
                 gateway=None,
                 ipdevExt="v4",
                 **kwargs):
        self.ipdevExt = ipdevExt
        Res.Ip.__init__(self,
                        rid=rid,
                        ipdev=ipdev,
                        ipname=ipname,
                        mask=mask,
                        gateway=gateway,
                        **kwargs)
        self.label = self.label + "/" + self.ipdevExt
        if not which('ipadm'):
            raise ex.excInitError("crossbow ips are not supported on this system")
        if 'noalias' not in self.tags:
            self.tags.add('noalias')

    def stopip_cmd(self):
        ret, out, err = (0, '', '')
        if self.gateway is not None:
            cmd=['route', '-q', 'delete', 'default', self.gateway]
            r, o, e = self.call(cmd, info=True, outlog=False, errlog=False)
            ret += r
        cmd=['ipadm', 'delete-addr', self.stacked_dev+'/'+self.ipdevExt]
        r, o, e =  self.vcall(cmd)
        ret += r
        out += o
        err += e
        cmd = ['ipadm', 'show-addr', '-p', '-o', 'state', self.stacked_dev ]
        p = Popen(cmd, stdin=None, stdout=PIPE, stderr=PIPE, close_fds=True)
        _out = bdecode(p.communicate()[0]).strip().split("\n")
        if len(_out) > 0:
            self.log.info("skip delete-ip because addrs still use the ip")
            return ret, out, err
        cmd=['ipadm', 'delete-ip', self.stacked_dev]
        r, o, e =  self.vcall(cmd)
        ret += r
        out += o
        err += e
        return ret, out, err

    def wait_net_smf(self, max_wait=30):
        r = 0
        prev_s = None
        while True:
            s = self.get_smf_status("network/routing-setup")
            if s == "online":
                break
            if s != prev_s or prev_s is None:
                self.log.info("waiting for network/routing-setup online state. current state: %s" % s)
            prev_s = s
            r += 1
            if r > max_wait:
                self.log.error("timeout waiting for network/routing-setup online state")
                break
            time.sleep(1)

    def get_smf_status(self, fmri):
        cmd = ["/usr/bin/svcs", "-H", "-o", "state", fmri]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            return "undef"
        return bdecode(out).strip()

    def startip_cmd(self):
        self.wait_net_smf()
        ret, out, err = (0, '', '')
        cmd = ['ipadm', 'show-if', '-p', '-o', 'state', self.stacked_dev]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE, close_fds=True)
        _out = bdecode(p.communicate()[0]).strip().split("\n")
        if len(_out) == 0:
            cmd=['ipadm', 'create-ip', '-t', self.stacked_dev ]
            r, o, e = self.vcall(cmd)
        cmd=['ipadm', 'create-addr', '-t', '-T', 'static', '-a', self.addr+"/"+to_cidr(self.mask), self.stacked_dev+'/'+self.ipdevExt]
        r, o, e = self.vcall(cmd)
        if r != 0:
            cmd=['ipadm', 'show-if' ]
            self.vcall(cmd)
            raise ex.excError("Interface %s is not up. ipadm cannot create-addr over it. Retrying..." % self.stacked_dev)
        ret += r
        out += o
        err += e
        if self.gateway is not None:
            cmd=['route', '-q', 'add', 'default', self.gateway]
            r, o, e = self.call(cmd, info=True, outlog=False, errlog=False)
            ret += r
        return ret, out, err

    def allow_start(self):
        if 'noaction' in self.tags:
            raise ex.IpNoActions(self.addr)
        retry = 10
        interval = 3
        import time
        ok = False
        if self.is_up() is True:
            self.log.info("%s is already up on %s" % (self.addr, self.ipdev))
            raise ex.IpAlreadyUp(self.addr)
        if not hasattr(self, 'abort_start_done') and 'nonrouted' not in self.tags and self.check_ping():
            self.log.error("%s is already up on another host" % (self.addr))
            raise ex.IpConflict(self.addr)

    def is_up(self):
        cmd = ["ipadm", "show-addr", "-p", "-o", "STATE,ADDR", self.ipdev+'/'+self.ipdevExt]
        out, err, ret = justcall(cmd)
        if ret != 0:
            # normal down state
            return False
        try:
            state, addr = out.strip("\n").split(":")
        except ValueError:
            self.status_log(out)
            return False
        if state != "ok":
            self.status_log("state: %s" % state)
            return False
        try:
            _addr, _mask = addr.split("/")
        except ValueError:
            self.status_log(out)
            return False
        if _addr != self.addr:
            self.status_log("wrong addr: %s" % addr)
            return False
        if self.mask is not None and _mask != to_cidr(self.mask):
            self.status_log("wrong mask: %s" % addr)
            return True
        return True

0707010005c9db000081b40000000000000000000000015ab2175400000385000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/resIpDarwin.py import resIp as Res
import rcExceptions as ex
from rcUtilitiesFreeBSD import check_ping
from rcUtilities import to_cidr

class Ip(Res.Ip):
    def check_ping(self, count=1, timeout=5):
        self.log.info("checking %s availability"%self.addr)
        return check_ping(self.addr, count=count, timeout=timeout)

    def arp_announce(self):
        return

    def startip_cmd(self):
        if ':' in self.addr:
            cmd = ['ifconfig', self.ipdev, 'inet6', '/'.join([self.addr, to_cidr(self.mask)]), 'add']
        else:
            cmd = ['ifconfig', self.ipdev, 'inet', self.addr, 'netmask', '0xffffffff', 'add']
        return self.vcall(cmd)

    def stopip_cmd(self):
        if ':' in self.addr:
            cmd = ['ifconfig', self.ipdev, 'inet6', self.addr, 'delete']
        else:
            cmd = ['ifconfig', self.ipdev, 'inet', self.addr, 'delete']
        return self.vcall(cmd)

   0707010005c9dc000081b40000000000000000000000015ab21754000035b0000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/resIpDockerLinux.py    import os
import resIpLinux as Res
import rcExceptions as ex
import rcIfconfigLinux as rcIfconfig
import rcStatus
from rcGlobalEnv import rcEnv
from rcUtilitiesLinux import check_ping
from rcUtilities import which, justcall, to_cidr, lazy

class Ip(Res.Ip):
    def __init__(self,
                 rid=None,
                 ipdev=None,
                 ipname=None,
                 mask=None,
                 gateway=None,
                 network=None,
                 del_net_route=False,
                 container_rid=None,
                 **kwargs):
        Res.Ip.__init__(self,
                        rid,
                        type="ip.docker",
                        ipdev=ipdev,
                        ipname=ipname,
                        gateway=gateway,
                        mask=mask,
                        **kwargs)
        self.network = network
        self.del_net_route = del_net_route
        self.container_rid = str(container_rid)
        self.label = str(ipname) + '@' + ipdev + '@' + self.container_rid
        self.tags = self.tags | set(["docker"])
        self.tags.add(container_rid)

    def on_add(self):
        self.svc.register_dependency("start", self.rid, self.container_rid)
        self.svc.register_dependency("start", self.container_rid, self.rid)
        self.svc.register_dependency("stop", self.container_rid, self.rid)

    @lazy
    def guest_dev(self):
        """
        Find a free eth netdev.

        Execute a ip link command in the container net namespace to parse
        used eth netdevs.
        """
        cmd = [rcEnv.syspaths.nsenter, "--net="+self.sandboxkey, "ip" , "link"]
        out, err, ret = justcall(cmd)
        used = []
        for line in out.splitlines():
            if ": eth" not in line:
                continue
            idx = line.split()[1].replace(":", "").replace("eth", "")
            if "@" in idx:
                # strip "@if<n>" suffix
                idx = idx[:idx.index("@")]
            used.append(int(idx))
        idx = 0
        while True:
            idx += 1
            if idx not in used:
                return "eth%d" % idx

    @lazy
    def container(self):
        if self.container_rid not in self.svc.resources_by_id:
            raise ex.excError("rid %s not found" % self.container_rid)
        return self.svc.resources_by_id[self.container_rid]

    def container_id(self, refresh=False):
        return self.svc.dockerlib.get_container_id_by_name(self.container, refresh=refresh)

    def arp_announce(self):
        """ disable the generic arping. We do that in the guest namespace.
        """
        pass

    def get_docker_ifconfig(self):
        try:
            nspid = self.get_nspid()
        except ex.excError as e:
            return
        if nspid is None:
            return

        cmd = [rcEnv.syspaths.nsenter, "--net="+self.sandboxkey, "ip", "addr"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return

        ifconfig = rcIfconfig.ifconfig(ip_out=out)
        return ifconfig

    def abort_start(self):
        if not self.container.docker_service:
            return Res.Ip.abort_start(self)
        return False

    def is_up(self):
        if not self.container.is_up():
            return False
        ifconfig = self.get_docker_ifconfig()
        if ifconfig is None:
            return False
        if ifconfig.has_param("ipaddr", self.addr):
            return True
        return False

    def get_docker_interface(self):
        ifconfig = self.get_docker_ifconfig()
        if ifconfig is None:
            return
        for intf in ifconfig.intf:
            if self.addr in intf.ipaddr+intf.ip6addr:
                name = intf.name
                if "@" in name:
                    name = name[:name.index("@")]
                return name
        return

    def container_running_elsewhere(self):
        if not self.container.docker_service:
            return False
        if len(self.container.service_hosted_instances()) == 0 and \
           len(self.container.service_running_instances()) > 0:
            return True
        return False

    def _status(self, verbose=False):
        self.unset_lazy("sandboxkey")
        if self.container_running_elsewhere():
            self.status_log("%s is hosted by another host" % self.container_rid, "info")
            return rcStatus.NA
        ret = Res.Ip._status(self)
        if self.container.docker_service and ret == rcStatus.DOWN:
            if check_ping(self.addr, timeout=1, count=1):
                return rcStatus.STDBY_UP
            else:
                self.status_log("ip is not up in the swarm. declare ourself 'stdby down' so we can takeover.")
                return rcStatus.STDBY_DOWN
        return ret

    def startip_cmd(self):
        self.unset_lazy("sandboxkey")
        if self.container.docker_service and \
           self._status() != rcStatus.STDBY_DOWN:
            return 0, "", ""
        if self.container_running_elsewhere():
            return 0, "", ""

        if "dedicated" in self.tags:
            self.log.info("dedicated mode")
            return self.startip_cmd_dedicated()
        else:
            return self.startip_cmd_shared()

    def startip_cmd_shared(self):
        if os.path.exists("/sys/class/net/%s/bridge" % self.ipdev):
            self.log.info("bridge mode")
            return self.startip_cmd_shared_bridge()
        else:
            self.log.info("macvlan mode")
            return self.startip_cmd_shared_macvlan()

    def startip_cmd_dedicated(self):
        # assign interface to the nspid
        cmd = [rcEnv.syspaths.ip, "link", "set", self.ipdev, "netns", nspid, "name", self.guest_dev]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            return ret, out, err

        # plumb the ip
        cmd = [rcEnv.syspaths.nsenter, "--net="+self.sandboxkey, "ip", "addr", "add", "%s/%s" % (self.addr, to_cidr(self.mask)), "dev", self.guest_dev]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            return ret, out, err

        # activate
        cmd = [rcEnv.syspaths.nsenter, "--net="+self.sandboxkey, "ip", "link", "set", self.guest_dev, "up"]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            return ret, out, err

        # add default route
        if self.gateway:
            cmd = [rcEnv.syspaths.nsenter, "--net="+self.sandboxkey, "ip", "route", "add", "default", "via", self.gateway, "dev", self.guest_dev]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                return ret, out, err

        # announce
        if which("arping") is not None:
            cmd = [rcEnv.syspaths.nsenter, "--net="+self.sandboxkey, "arping" , "-c", "1", "-A", "-I", self.guest_dev, self.addr]
            self.log.info(" ".join(cmd))
            out, err, ret = justcall(cmd)

        return 0, "", ""

    def startip_cmd_shared_bridge(self):
        nspid = self.get_nspid()
        tmp_guest_dev = "v%spg%s" % (self.guest_dev, nspid)
        tmp_local_dev = "v%spl%s" % (self.guest_dev, nspid)
        mtu = self.ip_get_mtu()

        # create peer devs
        cmd = [rcEnv.syspaths.ip, "link", "add", "name", tmp_local_dev, "mtu", mtu, "type", "veth", "peer", "name", tmp_guest_dev, "mtu", mtu]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            return ret, out, err

        # activate the parent dev
        cmd = [rcEnv.syspaths.ip, "link", "set", tmp_local_dev, "master", self.ipdev]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            return ret, out, err
        cmd = [rcEnv.syspaths.ip, "link", "set", tmp_local_dev, "up"]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            return ret, out, err

        # assign the macvlan interface to the container namespace
        cmd = [rcEnv.syspaths.ip, "link", "set", tmp_guest_dev, "netns", nspid]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            return ret, out, err

        # rename the tmp guest dev
        cmd = [rcEnv.syspaths.nsenter, "--net="+self.sandboxkey, "ip", "link", "set", tmp_guest_dev, "name", self.guest_dev]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            return ret, out, err

        # plumb ip
        cmd = [rcEnv.syspaths.nsenter, "--net="+self.sandboxkey, "ip", "addr", "add", self.addr+"/"+to_cidr(self.mask), "dev", self.guest_dev]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            return ret, out, err

        # setup default route
        self.ip_setup_route()

        self.ip_wait()
        return 0, "", ""

    def startip_cmd_shared_macvlan(self):
        nspid = self.get_nspid()

        tmp_guest_dev = "ph%s%s" % (nspid, self.guest_dev)
        mtu = self.ip_get_mtu()

        # create a macvlan interface
        cmd = [rcEnv.syspaths.ip, "link", "add", "link", self.ipdev, "dev", tmp_guest_dev, "mtu", mtu, "type", "macvlan", "mode", "bridge"]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            return ret, out, err

        # activate the parent dev
        cmd = [rcEnv.syspaths.ip, "link", "set", self.ipdev, "up"]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            return ret, out, err

        # assign the macvlan interface to the container namespace
        cmd = [rcEnv.syspaths.ip, "link", "set", tmp_guest_dev, "netns", nspid]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            return ret, out, err

        # rename the tmp guest dev
        cmd = [rcEnv.syspaths.nsenter, "--net="+self.sandboxkey, "ip", "link", "set", tmp_guest_dev, "name", self.guest_dev]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            return ret, out, err

        # plumb the ip
        cmd = [rcEnv.syspaths.nsenter, "--net="+self.sandboxkey, "ip", "addr", "add", "%s/%s" % (self.addr, to_cidr(self.mask)), "dev", self.guest_dev]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            return ret, out, err

        # setup default route
        self.ip_setup_route()

        self.ip_wait()
        return 0, "", ""

    def ip_get_mtu(self):
        # get mtu
        cmd = [rcEnv.syspaths.ip, "link", "show", self.ipdev]
        ret, out, err = self.call(cmd)
        if ret != 0:
            raise ex.excError("failed to get %s mtu: %s" % (self.ipdev, err))
        mtu = out.split()[4]
        return mtu

    def ip_setup_route(self):
        cmd = [rcEnv.syspaths.nsenter, "--net="+self.sandboxkey, "ip", "route", "del", "default"]
        ret, out, err = self.call(cmd, errlog=False)

        cmd = [rcEnv.syspaths.nsenter, "--net="+self.sandboxkey, "ip", "link", "set", self.guest_dev, "up"]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            return ret, out, err

        if self.gateway:
            cmd = [rcEnv.syspaths.nsenter, "--net="+self.sandboxkey, "ip", "route", "replace", "default", "via", self.gateway]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                return ret, out, err

        if self.del_net_route and self.network:
            cmd = [rcEnv.syspaths.nsenter, "--net="+self.sandboxkey, "ip", "route", "del", self.network+"/"+to_cidr(self.mask), "dev", self.guest_dev]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                return ret, out, err

        # announce
        if which("arping") is not None:
            cmd = [rcEnv.syspaths.nsenter, "--net="+self.sandboxkey, "arping" , "-c", "1", "-A", "-I", self.guest_dev, self.addr]
            self.log.info(" ".join(cmd))
            out, err, ret = justcall(cmd)

    def ip_wait(self):
        # ip activation may still be incomplete
        # wait for activation, to avoid startapp scripts to fail binding their listeners
        for _ in range(15, 0, -1):
            if check_ping(self.addr, timeout=1, count=1):
                return
        raise ex.excError("timed out waiting for ip activation")

    def get_nspid(self):
        container_id = self.container_id(refresh=True)
        if container_id is None:
            return
        cmd = self.svc.dockerlib.docker_cmd + ["inspect", "--format='{{ .State.Pid }}'", container_id]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError("failed to get nspid: %s" % err)
        nspid = out.strip()
        if "'" in nspid:
            nspid = nspid.replace("'","")
        if nspid == "0":
            raise ex.excError("nspid is 0")
        return nspid

    @lazy
    def sandboxkey(self):
        container_id = self.container_id(refresh=True)
        if container_id is None:
            return
        cmd = self.svc.dockerlib.docker_cmd + ["inspect", "--format='{{ .NetworkSettings.SandboxKey }}'", container_id]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError("failed to get sandboxkey: %s" % err)
        sandboxkey = out.strip()
        if "'" in sandboxkey:
            sandboxkey = sandboxkey.replace("'","")
        if sandboxkey == "":
            raise ex.excError("sandboxkey is empty")
        return sandboxkey

    def stopip_cmd(self):
        intf = self.get_docker_interface()
        if intf is None:
            raise ex.excContinueAction("can't find on which interface %s is plumbed in container %s" % (self.addr, self.container_id()))
        if self.mask is None:
            raise ex.excContinueAction("netmask is not set")
        cmd = [rcEnv.syspaths.nsenter, "--net="+self.sandboxkey, "ip", "addr", "del", self.addr+"/"+to_cidr(self.mask), "dev", intf]
        ret, out, err = self.vcall(cmd)
        self.unset_lazy("sandboxkey")
        return ret, out, err

0707010005c9dd000081b40000000000000000000000015ab2175400000398000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/resIpFreeBSD.py    import resIp as Res
import rcExceptions as ex
from rcUtilitiesFreeBSD import check_ping
from rcUtilities import to_cidr, to_dotted

class Ip(Res.Ip):
    def check_ping(self, count=1, timeout=5):
        self.log.info("checking %s availability"%self.addr)
        return check_ping(self.addr, count=count, timeout=timeout)

    def arp_announce(self):
        return

    def startip_cmd(self):
        if ':' in self.addr:
            cmd = ['ifconfig', self.ipdev, 'inet6', '/'.join([self.addr, to_cidr(self.mask)]), 'add']
        else:
            cmd = ['ifconfig', self.ipdev, 'inet', self.addr, 'netmask', to_dotted(self.mask), 'add']
        return self.vcall(cmd)

    def stopip_cmd(self):
        if ':' in self.addr:
            cmd = ['ifconfig', self.ipdev, 'inet6', self.addr, 'delete']
        else:
            cmd = ['ifconfig', self.ipdev, 'inet', self.addr, 'delete']
        return self.vcall(cmd)

0707010005c9de000081b40000000000000000000000015ab2175400001861000000660000000000000000000000000000002700000000root/usr/share/opensvc/lib/resIpGce.py    import resIp
import os
import rcStatus
from rcGlobalEnv import rcEnv
import rcExceptions as ex
from rcUtilities import getaddr, justcall
import json
import rcGce

rcIfconfig = __import__('rcIfconfig'+rcEnv.sysname)

class Ip(resIp.Ip, rcGce.Gce):
    def __init__(self,
                 rid=None,
                 ipname=None,
                 ipdev=None,
                 routename=None,
                 gce_zone=None,
                 **kwargs):
        resIp.Ip.__init__(self,
                          rid=rid,
                          ipname=ipname,
                          ipdev=ipdev,
                          **kwargs)
        self.label = "gce ip %s@%s" % (ipname, ipdev)
        self.routename = routename
        self.gce_zone = gce_zone

        # cache for route data
        self.gce_route_data = None

    def start_local_route(self):
        if self.has_local_route():
            self.log.info("ip route %s/32 dev %s is already installed" % (self.addr, self.ipdev))
            return
        self.add_local_route()

    def stop_local_route(self):
        if not self.has_local_route():
            self.log.info("ip route %s/32 dev %s is already uninstalled" % (self.addr, self.ipdev))
            return
        self.del_local_route()

    def add_local_route(self):
        cmd = ["ip", "route", "replace", self.addr+"/32", "dev", self.ipdev]
        self.vcall(cmd)

    def del_local_route(self):
        cmd = ["ip", "route", "del", self.addr+"/32", "dev", self.ipdev]
        self.vcall(cmd)

    def has_local_route(self):
        cmd = ["ip", "route", "list", self.addr+"/32", "dev", self.ipdev]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return False
        if out == "":
            return False
        return True

    def start_gce_route(self):
        if not self.routename:
            return
        if self.has_gce_route():
            self.log.info("gce route %s, %s to instance %s is already installed" % (self.routename, self.addr, rcEnv.nodename))
            return
        if self.exist_gce_route():
            self.del_gce_route()
        self.add_gce_route()
        self.svc.gce_routes_cache[self.routename] = {
          "destRange": self.addr+"/32",
          "nextHopInstance": rcEnv.nodename,
        }

    def stop_gce_route(self):
        if not self.routename:
            return
        if not self.has_gce_route():
            self.log.info("gce route %s, %s to instance %s is already uninstalled" % (self.routename, self.addr, rcEnv.nodename))
            return
        self.del_gce_route()
        self.get_gce_routes_list(refresh=True)
        del(self.svc.gce_routes_cache[self.routename])

    def add_gce_route(self):
        cmd = ["gcloud", "compute", "routes", "-q", "create", self.routename,
               "--destination-range", self.addr+"/32",
               "--next-hop-instance", rcEnv.nodename,
               "--next-hop-instance-zone", self.gce_zone]
        self.vcall(cmd)

    def del_gce_route(self):
        cmd = ["gcloud", "compute", "routes", "-q", "delete", self.routename]
        self.vcall(cmd)

    def get_gce_route_data(self, refresh=False):
        data = self.get_gce_routes_list(refresh=refresh)
        if data is None:
            return
        if not self.routename in data:
            return
        return data[self.routename]

    def get_gce_routes_list(self, refresh=False):
        if not refresh and hasattr(self.svc, "gce_routes_cache"):
            return self.svc.gce_routes_cache
        self.svc.gce_routes_cache = self._get_gce_routes_list()
        return self.svc.gce_routes_cache

    def _get_gce_routes_list(self):
        if not self.routename:
            return
        routenames = " ".join([r.routename for r in self.svc.get_resources("ip") if hasattr(r, "routename")])
        self.wait_gce_auth()
        cmd = ["gcloud", "compute", "routes", "list", "--format", "json", routenames]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError("gcloud route describe returned with error: %s, %s" % (out, err))
        try:
            data = json.loads(out)
        except:
            raise ex.excError("unable to parse gce route data: %s" % out)
        h = {}
        for route in data:
            h[route["name"]] = route
        return h

    def exist_gce_route(self):
        if not self.routename:
            return True
        data = self.get_gce_route_data()
        if not data:
            return False
        if data:
            return True
        return False

    def has_gce_route(self):
        if not self.routename:
            return True
        data = self.get_gce_route_data()
        if not data:
            return False
        if data.get("destRange") != self.addr+"/32":
            return False
        if data.get("nextHopInstance").split("/")[-1] != rcEnv.nodename:
            return False
        return True

    def is_up(self):
        """Returns True if ip is associated with this node
        """
        self.getaddr()
        if not self.has_local_route():
            return False
        if not self.has_gce_route():
            return False
        return True

    def _status(self, verbose=False):
        self.getaddr()
        try:
            local_status = self.has_local_route()
            if not local_status:
                self.status_log("local route is not installed")
        except ex.excError as e:
            self.status_log(str(e))
            local_status = False

        try:
            gce_status = self.has_gce_route()
            if not gce_status:
                self.status_log("gce route is not installed")
        except ex.excError as e:
            self.status_log(str(e))
            gce_status = False

        s = local_status & gce_status
        if s:
            return rcStatus.UP
        else:
            return rcStatus.DOWN

    def check_ping(self, count=1, timeout=5):
        pass

    def start(self):
        self.getaddr()
        self.start_local_route()
        self.start_gce_route()

    def stop(self):
        self.getaddr()
        self.stop_local_route()
        # don't unconfigure the gce route: too long. let the start replace it if necessary.

   0707010005c9df000081b40000000000000000000000015ab2175400000458000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/resIpHP-UX.py  import resIp as Res
u = __import__('rcUtilitiesHP-UX')
from rcUtilities import to_cidr, to_dotted
import rcExceptions as ex

class Ip(Res.Ip):
    def check_ping(self, count=1, timeout=5):
        self.log.info("checking %s availability"%self.addr)
        return u.check_ping(self.addr, count=count, timeout=timeout)

    def arp_announce(self):
        """ arp_announce job is done by HP-UX ifconfig... """
        return

    def startip_cmd(self):
        if ':' in self.addr:
            cmd = ['ifconfig', self.ipdev, 'inet6', 'up']
            (ret, out, err) = self.vcall(cmd)
            if ret != 0:
                raise ex.excError
            cmd = ['ifconfig', self.stacked_dev, 'inet6', self.addr+'/'+to_cidr(self.mask), 'up']
        else:
            cmd = ['ifconfig', self.stacked_dev, self.addr, 'netmask', to_dotted(self.mask), 'up']
        return self.vcall(cmd)

    def stopip_cmd(self):
        if ':' in self.addr:
            cmd = ['ifconfig', self.stacked_dev, "inet6", "::"]
        else:
            cmd = ['ifconfig', self.stacked_dev, "0.0.0.0"]
        return self.vcall(cmd)

0707010005c9e0000081b40000000000000000000000015ab21754000006c4000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/resIpLinux.py  import resIp as Res
import rcExceptions as ex
from rcGlobalEnv import rcEnv
from rcUtilitiesLinux import check_ping
from rcUtilities import which, to_cidr, to_dotted

class Ip(Res.Ip):
    def check_ping(self, timeout=5, count=1):
        self.log.info("checking %s availability"%self.addr)
        return check_ping(self.addr, timeout=timeout, count=count)

    def start_link(self):
        if which(rcEnv.syspaths.ip):
           cmd = [rcEnv.syspaths.ip, 'link', 'set', 'dev', self.ipdev, 'up']
        else:
           cmd = ['ifconfig', self.ipdev, 'up']
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            return ret, out, err

    def startip_cmd(self):
        if ':' in self.addr:
            cmd = ['ifconfig', self.ipdev, 'inet6', 'add', '/'.join([self.addr, to_cidr(self.mask)])]
        else:
            cmd = ['ifconfig', self.stacked_dev, self.addr, 'netmask', to_dotted(self.mask), 'up']

        ret, out, err = self.vcall(cmd)
        if ret != 0:
            return ret, out, err

        # ip activation may still be incomplete
        # wait for activation, to avoid startapp scripts to fail binding their listeners
        for i in range(5, 0, -1):
            if check_ping(self.addr, timeout=1, count=1):
                return ret, out, err
        self.log.error("timed out waiting for ip activation")
        raise ex.excError

    def stopip_cmd(self):
        if ':' in self.addr:
            cmd = ['ifconfig', self.ipdev, 'inet6', 'del', '/'.join([self.addr, to_cidr(self.mask)])]
        else:
            if self.stacked_dev is None:
                return 1, "", "no stacked dev found"
            cmd = ['ifconfig', self.stacked_dev, 'down']
        return self.vcall(cmd)

0707010005c9e1000081b40000000000000000000000015ab2175400000397000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/resIpOSF1.py   import resIp as Res
import rcExceptions as ex
from rcUtilitiesOSF1 import check_ping
from rcUtilities import to_cidr, to_dotted

class Ip(Res.Ip):
    def check_ping(self, count=1, timeout=5):
        self.log.info("checking %s availability"%self.addr)
        return check_ping(self.addr, count=count, timeout=timeout)

    def arp_announce(self):
        return

    def startip_cmd(self):
        if ':' in self.addr:
            cmd = ['ifconfig', self.ipdev, 'inet6', '/'.join([self.addr, to_cidr(self.mask)]), 'add']
        else:
            cmd = ['ifconfig', self.ipdev, 'inet', 'alias', self.addr, 'netmask', to_dotted(self.mask)]
        return self.vcall(cmd)

    def stopip_cmd(self):
        if ':' in self.addr:
            cmd = ['ifconfig', self.ipdev, 'inet6', self.addr, 'delete']
        else:
            cmd = ['ifconfig', self.ipdev, 'inet', '-alias', self.addr]
        return self.vcall(cmd)

 0707010005c9e2000081b40000000000000000000000015ab2175400000310000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/resIpSunOS.py  import resIp as Res
from subprocess import *
from rcUtilitiesSunOS import check_ping
import rcExceptions as ex

class Ip(Res.Ip):
    """
    SunOS ip resource driver.
    """

    def arp_announce(self):
        """
        Noop becauce the arp_announce job is done by SunOS ifconfig
        """
        return

    def check_ping(self, count=1, timeout=2):
        self.log.info("checking %s availability"%self.addr)
        return check_ping(self.addr, timeout=timeout)

    def startip_cmd(self):
        cmd=['/usr/sbin/ifconfig', self.stacked_dev, 'plumb', self.addr, \
            'netmask', '+', 'broadcast', '+', 'up']
        return self.vcall(cmd)

    def stopip_cmd(self):
        cmd = ['/usr/sbin/ifconfig', self.stacked_dev, 'unplumb']
        return self.vcall(cmd)

0707010005c9e3000081b40000000000000000000000015ab2175400000a0d000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/resIpWindows.py    #
# Copyright (c) 2009 Christophe Varoqui <christophe.varoqui@free.fr>'
# Copyright (c) 2009 Cyril Galibern <cyril.galibern@free.fr>'
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# To change this template, choose Tools | Templates
# and open the template in the editor.
"Module implement Windows specific ip management"

import resIp as Res
import rcExceptions as ex
from rcUtilitiesWindows import check_ping

class Ip(Res.Ip):
    def check_ping(self, timeout=5, count=1):
        self.log.info("checking %s availability"%self.addr)
        return check_ping(self.addr, timeout=timeout, count=count)

#netsh interface ip add address "Local Area Connection" 33.33.33.33 255.255.255.255

    def startip_cmd(self):
        if ':' in self.addr:
            if '.' in self.mask:
                self.log.error("netmask parameter is mandatory for ipv6 adresses")
                raise ex.excError
            cmd = ['netsh', 'interface', 'ipv6', 'add', 'address', self.ipDev, self.addr, self.mask]
        else:
            cmd = ['netsh', 'interface', 'ipv4', 'add', 'address', self.ipDev, self.addr, self.mask]

        ret, out, err = self.vcall(cmd)
        if ret != 0:
            return ret, out, err

        # ip activation may still be incomplete
        # wait for activation, to avoid startapp scripts to fail binding their listeners
        for i in range(5, 0, -1):
            if check_ping(self.addr, timeout=1, count=1):
                return ret, out, err
        self.log.error("timed out waiting for ip activation")
        raise ex.excError

    def stopip_cmd(self):
        if ':' in self.addr:
            cmd = ['netsh', 'interface', 'ipv6', 'delete', 'address', self.ipDev, "addr="+self.addr]
        else:
            cmd = ['netsh', 'interface', 'ipv4', 'delete', 'address', self.ipDev, "addr="+self.addr]
        return self.vcall(cmd)



if __name__ == "__main__":
    for c in (Ip,) :
        help(c)

   0707010005c9e4000081b40000000000000000000000015ab217540000083e000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/resIpZone.py   import time
import resIpSunOS as Res
import rcExceptions as ex
from subprocess import *
from rcGlobalEnv import rcEnv
rcIfconfig = __import__('rcIfconfig'+rcEnv.sysname)

class Ip(Res.Ip):
    def __init__(self,
                 rid=None,
                 ipdev=None,
                 ipname=None,
                 zone=None,
                 mask=None,
                 gateway=None,
                 **kwargs):
        Res.Ip.__init__(self,
                        rid=rid,
                        ipdev=ipdev,
                        ipname=ipname,
                        mask=mask,
                        gateway=gateway,
                        **kwargs)
        self.zone = zone
        self.tags.add(zone)
        self.tags.add('zone')

    def startip_cmd(self):
        cmd=['ifconfig', self.stacked_dev, 'plumb', self.addr, \
             'netmask', '+', 'broadcast', '+', 'up' , 'zone' , self.zone ]
        return self.vcall(cmd)

    def stopip_cmd(self):
        cmd=['ifconfig', self.stacked_dev, 'unplumb']
        return self.vcall(cmd)

    def allow_start(self):
        retry = 1
        interval = 0
        import time
        ok = False
        if 'noalias' not in self.tags:
            for i in range(retry):
                ifconfig = rcIfconfig.ifconfig()
                intf = ifconfig.interface(self.ipdev)
                if intf is not None and intf.flag_up:
                    ok = True
                    break
                time.sleep(interval)
            if not ok:
                self.log.error("Interface %s is not up. Cannot stack over it." % self.ipdev)
                raise ex.IpDevDown(self.ipdev)
        if self.is_up() is True:
            self.log.info("%s is already up on %s" % (self.addr, self.ipdev))
            raise ex.IpAlreadyUp(self.addr)
        if not hasattr(self, 'abort_start_done') and 'nonrouted' not in self.tags and self.check_ping():
            self.log.error("%s is already up on another host" % (self.addr))
            raise ex.IpConflict(self.addr)
        return

if __name__ == "__main__":
    for c in (Ip,) :
        help(c)

  0707010005c9e5000081b40000000000000000000000015ab2175400002281000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/resScsiReserv.py   import resources as Res
import uuid
import re
import os
import time
import rcStatus
import rcExceptions as ex
from rcUtilities import which
from subprocess import *
from rcGlobalEnv import rcEnv
hostId = __import__('hostid'+rcEnv.sysname)


class ScsiReserv(Res.Resource):
    """Define method to acquire and release scsi SPC-3 persistent reservations
    on devs held by a service
    """

    def __init__(self,
                 rid=None,
                 peer_resource=None,
                 no_preempt_abort=False,
                 prkey=None,
                 **kwargs):
        self.no_preempt_abort = no_preempt_abort
        self.devs = set()
        self.preempt_timeout = 10
        self.prtype = '5'
        self.hostid = None
        self.peer_resource = peer_resource
        self.prkey = prkey
        Res.Resource.__init__(self,
                              rid=rid+"pr",
                              type="disk.scsireserv",
                              **kwargs)

    def mangle_devs(self, devs):
        """
        Can be overidden by child class to apply a mangling the peer
        resource devices
        """
        return devs

    def set_label(self):
        self.get_devs()
        if len(self.devs) == 0:
            self.label = 'preserv 0 scsi disk'
        elif len(', '.join(self.devs)) > 248:
            self.label = 'preserv '+', '.join(self.devs)[0:248]
            self.label += " ..."
        else:
            self.label = ', '.join(self.devs)

    def get_hostid(self):
        if self.hostid:
            return
        if self.prkey:
            self.hostid = self.prkey
            return
        try:
            self.hostid = self.svc.node.get_prkey()
        except Exception as e:
            raise ex.excError(str(e))

    def _info(self):
        self.get_hostid()
        data = [
            ["prkey", self.hostid],
        ]
        return data

    def scsireserv_supported(self):
        return False

    def ack_unit_attention(self, d):
        raise ex.notImplemented

    def disk_registered(self, disk):
        raise ex.notImplemented

    def disk_register(self, disk):
        raise ex.notImplemented

    def disk_unregister(self, disk):
        raise ex.notImplemented

    def get_reservation_key(self, disk):
        raise ex.notImplemented

    def disk_reserved(self, disk):
        raise ex.notImplemented

    def disk_release(self, disk):
        raise ex.notImplemented

    def disk_reserve(self, disk):
        raise ex.notImplemented

    def disk_preempt_reservation(self, disk, oldkey):
        if not hasattr(self, '_disk_preempt_reservation'):
            raise ex.notImplemented
        if not self.svc.options.force and os.environ.get("OSVC_ACTION_ORIGIN") != "daemon":
            self.log.error("%s is already reserved. use --force to override this safety net"%disk)
            raise ex.excError
        return self._disk_preempt_reservation(disk, oldkey)

    def get_devs(self):
        if len(self.devs) > 0:
            return
        self.devs = self.peer_resource.sub_devs()
        self.devs = self.mangle_devs(self.devs)

    def ack_all_unit_attention(self):
        self.get_devs()
        for d in self.devs:
            try:
                if self.ack_unit_attention(d) != 0:
                    return 1
            except ex.excScsiPrNotsupported as exc:
                self.status_log(str(exc))
                continue
        return 0

    def register(self):
        self.log.debug("starting register. prkey %s"%self.hostid)
        self.get_devs()
        r = 0
        for d in self.devs:
            try:
                r += self.ack_unit_attention(d)
                r += self.disk_register(d)
            except ex.excScsiPrNotsupported as exc:
                self.log.error(str(exc))
                continue
        return r

    def unregister(self):
        self.log.debug("starting unregister. prkey %s"%self.hostid)
        self.get_devs()
        r = 0
        for d in self.devs:
            try:
                r += self.ack_unit_attention(d)
                if not self.disk_registered(d):
                    continue
                r += self.disk_unregister(d)
            except ex.excScsiPrNotsupported as exc:
                self.log.error(str(exc))
                continue
        return r

    def disk_wait_reservation(self, disk):
        for i in range(3, 0, -1):
            if self.disk_reserved(disk):
                self.log.info("reservation acquired for disk %s" % disk)
                return 0
            if i > 0:
                time.sleep(1)
        self.log.error("timed out waiting for reservation for disk %s" % disk)
        return 1

    def reserve(self):
        self.log.debug("starting reserve. prkey %s"%self.hostid)
        self.get_devs()
        r = 0
        for d in self.devs:
            try:
                r += self.ack_unit_attention(d)
                key = self.get_reservation_key(d)
                if key is None:
                    r += self.disk_reserve(d)
                elif key == self.hostid:
                    continue
                else:
                    r += self.disk_preempt_reservation(d, key)
                    r += self.disk_wait_reservation(d)
            except ex.excScsiPrNotsupported as exc:
                self.log.error(str(exc))
                continue
        return r

    def release(self):
        self.log.debug("starting release. prkey %s"%self.hostid)
        self.get_devs()
        r = 0
        for d in self.devs:
            try:
                r += self.ack_unit_attention(d)
                if not self.disk_reserved(d):
                    continue
                r += self.disk_release(d)
            except ex.excScsiPrNotsupported as exc:
                self.log.error(str(exc))
                continue
        return r

    def clear(self):
        self.log.debug("starting clear. prkey %s"%self.hostid)
        self.get_devs()
        r = 0
        for d in self.devs:
            try:
                r += self.ack_unit_attention(d)
                if not self.disk_reserved(d):
                    continue
                r += self.disk_clear_reservation(d)
            except ex.excScsiPrNotsupported as exc:
                self.log.error(str(exc))
                continue
        return r

    def checkreserv(self):
        self.log.debug("starting checkreserv. prkey %s"%self.hostid)
        if self.ack_all_unit_attention() != 0:
            return rcStatus.WARN
        r = rcStatus.Status()
        for d in self.devs:
            try:
                key = self.get_reservation_key(d)
                if key is None:
                    self.log.debug("disk %s is not reserved" % d)
                    r += rcStatus.DOWN
                elif key != self.hostid:
                    self.log.debug("disk %s is reserved by another host whose key is %s" % (d, key))
                    r += rcStatus.DOWN
                else:
                    self.log.debug("disk %s is correctly reserved" % d)
                    r += rcStatus.UP
            except ex.excScsiPrNotsupported as exc:
                self.log.error(str(exc))
                continue
        return r.status

    def scsireserv(self):
        self.get_hostid()
        if not self.scsireserv_supported():
            return
        r = 0
        r += self.register()
        r += self.reserve()
        return r

    def scsirelease(self):
        self.get_hostid()
        if not self.scsireserv_supported():
            return
        r = 0
        if hasattr(self, 'disk_clear_reservation'):
            r += self.clear()
        else:
            r += self.release()
            r += self.unregister()
        return r

    def scsicheckreserv(self):
        self.get_hostid()
        if not self.scsireserv_supported():
            return
        return self.checkreserv()

    def _status(self, verbose=False):
        self.set_label()
        try:
            self.get_hostid()
        except Exception as e:
            self.status_log(str(e))
            return rcStatus.WARN
        if not self.scsireserv_supported():
            self.status_log("scsi reservation is not supported")
            return rcStatus.NA
        return self.checkreserv()

    def start(self):
        self.get_hostid()
        if not self.scsireserv_supported():
            return
        if self._status() == rcStatus.UP:
            self.log.info("already started")
            return
        self.can_rollback = True
        if self.scsireserv() != 0:
            raise ex.excError

    def stop(self):
        self.get_hostid()
        if not self.scsireserv_supported():
            return
        if self.scsirelease() != 0:
            raise ex.excError

    def is_provisioned(self, refresh=False):
        return True
   0707010005c9e6000081b40000000000000000000000015ab2175400000101000000660000000000000000000000000000003300000000root/usr/share/opensvc/lib/resScsiReservFreeBSD.py    import resScsiReservSg
from rcUtilitiesLinux import dev_to_paths

class ScsiReserv(resScsiReservSg.ScsiReserv):
    def mangle_devs(self, devs):
        _devs = set()
        for dev in devs:
            _devs |= set(dev_to_paths(dev))
        return _devs
   0707010005c9e7000081b40000000000000000000000015ab21754000013a8000000660000000000000000000000000000003100000000root/usr/share/opensvc/lib/resScsiReservHP-UX.py  import resources as Res
import uuid
import re
import os
import time
import rcStatus
import rcExceptions as ex
from rcUtilities import which
from subprocess import *
import resScsiReserv

def mpath_to_path(disks):
    l = []
    for disk in disks:
        if "/dev/dsk" in disk:
            l.append(disk.replace("/dev/dsk", "/dev/rdsk"))
            continue
        if "/dev/disk" not in disk and "/dev/rdisk" not in disk:
            continue
        if not os.path.exists(disk):
            continue
        cmd = ['ioscan', '-F', '-m', 'dsf', disk]
        p = Popen(cmd, stderr=None, stdout=PIPE, close_fds=True)
        buff = p.communicate()
        ret = p.returncode
        if ret != 0:
            continue
        a = buff[0].split(':')
        if len(a) != 2:
            continue
        b = a[1].split()
        for d in b:
            l.append(d.replace("/dev/dsk", "/dev/rdsk"))
    return l

class ScsiReserv(resScsiReserv.ScsiReserv):
    def __init__(self,
                 rid=None,
                 peer_resource=None,
                 no_preempt_abort=False,
                 **kwargs):
        resScsiReserv.ScsiReserv.__init__(self,
                                          rid=rid,
                                          peer_resource=peer_resource,
                                          no_preempt_abort=no_preempt_abort,
                                          **kwargs)
        self.prtype = 'wero'
        self.leg_mpath_disable()

    def get_devs(self):
        self.devs = mpath_to_path(self.peer_resource.base_devs())

    def scsireserv_supported(self):
        if which('scu') is None:
            return False
        return True

    def leg_mpath_disable(self):
        cmd = ['scsimgr', 'get_attr', '-p', '-a', 'leg_mpath_enable']
        p = Popen(cmd, stderr=None, stdout=PIPE, close_fds=True)
        buff = p.communicate()
        ret = p.returncode
        if ret != 0:
            self.log.error("can not fetch 'leg_mpath_enable' value")
            raise ex.excError
        if 'false' in buff[0]:
            return
        cmd = ['scsimgr', 'save_attr', '-a', 'leg_mpath_enable=false']
        self.log.info(' '.join(cmd))
        p = Popen(cmd, stderr=None, stdout=PIPE, close_fds=True)
        buff = p.communicate()
        ret = p.returncode
        if ret != 0:
            self.log.error("can not set 'leg_mpath_enable' value")
            raise ex.excError

    def ack_unit_attention(self, d):
        return 0

    def disk_registered(self, disk):
        cmd = [ 'scu', '-f', disk, 'show', 'keys' ]
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            self.log.error("failed to read registrations for disk %s" % disk)
        if self.hostid in out:
            return True
        return False

    def disk_register(self, disk):
        cmd = [ 'scu', '-f', disk, 'preserve', 'register', 'skey', self.hostid ]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            self.log.error("failed to register key %s with disk %s" % (self.hostid, disk))
        return ret

    def disk_unregister(self, disk):
        cmd = [ 'scu', '-f', disk, 'preserve', 'register', 'skey', '0', 'key', self.hostid ]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            self.log.error("failed to unregister key %s with disk %s" % (self.hostid, disk))
        return ret

    def get_reservation_key(self, disk):
        cmd = [ 'scu', '-f', disk, 'show', 'reservation' ]
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            self.log.error("failed to list reservation for disk %s" % disk)
        if 'Reservation Key' not in out:
            return None
        for line in out.split('\n'):
            if 'Reservation Key' in line:
                return line.split()[-1]
        raise Exception()

    def disk_reserved(self, disk):
        cmd = [ 'scu', '-f', disk, 'show', 'reservation' ]
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            self.log.error("failed to read reservation for disk %s" % disk)
        if self.hostid in out:
            return True
        return False

    def disk_release(self, disk):
        cmd = [ 'scu', '-f', disk, 'preserve', 'release', 'key', self.hostid, 'type', self.prtype ]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            self.log.error("failed to release disk %s" % disk)
        return ret

    def disk_reserve(self, disk):
        cmd = [ 'scu', '-f', disk, 'preserve', 'reserve', 'key', self.hostid, 'type', self.prtype ]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            self.log.error("failed to reserve disk %s" % disk)
        return ret

    def _disk_preempt_reservation(self, disk, oldkey):
        cmd = [ 'scu', '-f', disk, 'preserve', 'preempt', 'key', self.hostid, 'skey', oldkey, 'type', self.prtype ]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            self.log.error("failed to preempt reservation for disk %s" % disk)
        return ret

0707010005c9e8000081b40000000000000000000000015ab2175400000101000000660000000000000000000000000000003100000000root/usr/share/opensvc/lib/resScsiReservLinux.py  import resScsiReservSg
from rcUtilitiesLinux import dev_to_paths

class ScsiReserv(resScsiReservSg.ScsiReserv):
    def mangle_devs(self, devs):
        _devs = set()
        for dev in devs:
            _devs |= set(dev_to_paths(dev))
        return _devs
   0707010005c9e9000081b40000000000000000000000015ab2175400001943000000660000000000000000000000000000003000000000root/usr/share/opensvc/lib/resScsiReservOSF1.py   import resources as Res
import uuid
import re
import os
import time
import rcStatus
import rcExceptions as ex
from rcUtilities import which
from subprocess import *
import resScsiReserv

class ScsiReserv(resScsiReserv.ScsiReserv):
    def __init__(self,
                 rid=None,
                 peer_resource=None,
                 no_preempt_abort=False,
                 prkey=None,
                 **kwargs):
        resScsiReserv.ScsiReserv.__init__(self,
                                          rid=rid,
                                          peer_resource=peer_resource,
                                          no_preempt_abort=no_preempt_abort,
                                          prkey=prkey,
                                          **kwargs)
        self.prtype = 'wero'
        self.disk_id = {}
        self.itn = {}

    def get_devs(self):
        if len(self.devs) > 0:
            return
        self.devs = map(lambda x: str(x.replace('/disk/', '/rdisk/')), self.peer_resource.sub_devs())

    def scsireserv_supported(self):
        if which('scu') is None:
            return False
        return True

    def ack_unit_attention(self, d):
        return 0

    def get_disk_ids(self):
        if len(self.disk_id) > 0:
            return
        cmd = [ 'hwmgr', 'show', 'scsi' ]
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            raise ex.excError(err)
        for line in out.split('\n'):
            v = line.split()
            if len(v) < 7:
                continue
            if v[3] != "disk":
                continue
            if not v[7].startswith("dsk"):
                continue
            self.disk_id[v[7]+'c'] = v[0].strip(":")

    def get_itns(self):
        if len(self.itn) > 0:
            return
        self.get_disk_ids()
        for disk in self.disk_id:
            self.get_itn(disk)

    def get_itn(self, disk):
        if disk in self.itn:
            return
        self.itn[disk] = []
        if disk not in self.disk_id:
            return
        id = self.disk_id[disk]
        cmd = [ 'hwmgr', 'show', 'scsi', '-id', id, '-full' ]
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            return
        for line in out.split('\n'):
            v = line.split()
            if len(v) != 4:
                continue
            data = {}
            for i, s in enumerate(("bus", "target", "lun")):
                try:
                    j = int(v[i])
                    data[s] = v[i]
                except:
                    continue
            self.itn[disk].append(data)

    def set_nexus(self, itn):
        return "set nexus bus %(bus)s target %(target)s lun %(lun)s ; " % itn

    def pipe_scu(self, cmd):
        self.log.info(cmd + ' | scu')
        p = Popen(['scu'], stdin=PIPE, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate(input=cmd)
        #if len(out) > 0:
        #    self.log.info(out)
        if len(err) > 0:
            self.log.error(out)
        if p.returncode:
            raise ex.excError

    def disk_registered(self, disk):
        cmd = [ 'scu', '-f', disk, 'show', 'keys' ]
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            self.log.error("failed to read registrations for disk %s" % disk)
        if out.count(self.hostid) == 0:
            return False
        return True

    def disk_register(self, disk):
        self.get_itns()
        basedisk = os.path.basename(disk)
        if basedisk not in self.itn:
            self.log.error("no nexus information for disk %s"%disk)
            return 1
        r = 0
        for itn in self.itn[basedisk]:
            r += self.__disk_register(itn)
        if r > 0:
            r = 1
        return r

    def __disk_register(self, itn):
        cmd = self.set_nexus(itn) + 'preserve register skey ' + self.hostid
        try:
            self.pipe_scu(cmd)
        except ex.excError as e:
            self.log.error("failed to register key %s with nexus %s" % (self.hostid, ':'.join(itn.values())))
            return 1
        return 0

    def disk_unregister(self, disk):
        self.get_itns()
        basedisk = os.path.basename(disk)
        if basedisk not in self.itn:
            self.log.error("no nexus information for disk %s"%disk)
            return 1
        r = 0
        for itn in self.itn[basedisk]:
            r += self.__disk_unregister(itn)
        if r > 0:
            r = 1
        return r

    def __disk_unregister(self, itn):
        cmd = self.set_nexus(itn) +  'preserve register skey 0 key ' + self.hostid
        try:
            self.pipe_scu(cmd)
        except ex.excError as e:
            self.log.error("failed to unregister key %s with nexus %s" % (self.hostid, ':'.join(itn.values())))
            return 1
        return 0

    def get_reservation_key(self, disk):
        cmd = [ 'scu', '-f', disk, 'show', 'reservation' ]
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            self.log.error("failed to list reservation for disk %s" % disk)
        if 'Reservation Key' not in out:
            return None
        for line in out.split('\n'):
            if 'Reservation Key' in line:
                return line.split()[-1]
        raise Exception()

    def disk_reserved(self, disk):
        cmd = [ 'scu', '-f', disk, 'show', 'reservation' ]
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            self.log.error("failed to read reservation for disk %s" % disk)
        if self.hostid in out:
            return True
        return False

    def disk_release(self, disk):
        cmd = [ 'scu', '-f', disk, 'preserve', 'release', 'key', self.hostid, 'type', self.prtype ]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            self.log.error("failed to release disk %s" % disk)
        return ret

    def disk_reserve(self, disk):
        cmd = [ 'scu', '-f', disk, 'preserve', 'reserve', 'key', self.hostid, 'type', self.prtype ]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            self.log.error("failed to reserve disk %s" % disk)
        return ret

    def _disk_preempt_reservation(self, disk, oldkey):
        cmd = [ 'scu', '-f', disk, 'preserve', 'preempt', 'key', self.hostid, 'skey', oldkey, 'type', self.prtype ]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            self.log.error("failed to preempt reservation for disk %s" % disk)
        return ret

 0707010005c9ea000081b40000000000000000000000015ab2175400001776000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/resScsiReservSg.py import os
import resources as Res
import uuid
import re
import time
import rcStatus
import rcExceptions as ex
from rcUtilities import which, bdecode
from subprocess import *
import resScsiReserv
from rcGlobalEnv import rcEnv


class ScsiReserv(resScsiReserv.ScsiReserv):
    def scsireserv_supported(self):
        if which('sg_persist') is None:
            self.status_log("sg_persist must be installed to use scsi-3 reservations" )
            return False
        return True

    def set_read_only(self, val):
        if rcEnv.sysname != "Linux":
            return
        os.environ["SG_PERSIST_O_RDONLY"] = str(val)
        os.environ["SG_PERSIST_IN_RDONLY"] = str(val)

    def ack_unit_attention(self, d):
        if not os.path.exists(d):
            return 0
        i = self.preempt_timeout
        self.set_read_only(0)
        while i>0:
            i -= 1
            cmd = [ 'sg_persist', '-n', '-r', d ]
            p = Popen(cmd, stdout=PIPE, stderr=PIPE, close_fds=True)
            out, err = p.communicate()
            out = bdecode(out)
            err = bdecode(err)
            ret = p.returncode
            if "unsupported service action" in err:
                raise ex.excScsiPrNotsupported("disk %s does not support persistent reservation" % d)
            if "error opening file" in err:
                return 0
            if "Unit Attention" in out or ret != 0:
                self.log.debug("disk %s reports 'Unit Attention' ... waiting" % d)
                time.sleep(1)
                continue
            break
        if i == 0:
            self.log.error("timed out waiting for 'Unit Attention' to go away on disk %s" % d)
            return 1
        return 0

    def disk_registered(self, disk):
        self.set_read_only(1)
        cmd = [ 'sg_persist', '-n', '-k', disk ]
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            self.log.error("failed to read registrations for disk %s" % disk)
        if self.hostid in out:
            return True
        return False

    def disk_register(self, disk):
        self.set_read_only(0)
        cmd = [ 'sg_persist', '-n', '--out', '--register-ignore', '--param-sark='+self.hostid, disk ]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            self.log.error("failed to register key %s with disk %s" % (self.hostid, disk))
        return ret

    def disk_unregister(self, disk):
        self.set_read_only(0)
        cmd = [ 'sg_persist', '-n', '--out', '--register-ignore', '--param-rk='+self.hostid, disk ]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            self.log.error("failed to unregister key %s with disk %s" % (self.hostid, disk))
        return ret

    def dev_to_mpath_dev(self, devpath):
        if which(rcEnv.syspaths.multipath) is None:
            return devpath
        cmd = [rcEnv.syspaths.multipath, "-l", "-v1", devpath]
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            raise ex.excError(err)
        _devpath = "/dev/mapper/"+out.strip()
        if not os.path.exists(_devpath):
            raise ex.excError("%s does not exist")
        return _devpath

    def get_reservation_key(self, disk):
        try:
            return self._get_reservation_key(disk)
        except ex.excError as e:
            disk = self.dev_to_mpath_dev(disk)
            return self._get_reservation_key(disk)

    def _get_reservation_key(self, disk):
        self.set_read_only(1)
        cmd = [ 'sg_persist', '-n', '-r', disk ]
        (ret, out, err) = self.call(cmd, errlog=None)
        if ret != 0:
            raise ex.excError("failed to list reservation for disk %s" % disk)
        if 'Key=' not in out:
            return None
        for w in out.split():
            if 'Key=' in w:
                return w.split('=')[1]
        raise Exception()

    def disk_reserved(self, disk):
        try:
            return self._disk_reserved(disk)
        except ex.excError as e:
            disk = self.dev_to_mpath_dev(disk)
            return self._disk_reserved(disk)

    def _disk_reserved(self, disk):
        self.set_read_only(1)
        cmd = [ 'sg_persist', '-n', '-r', disk ]
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            raise ex.excError("failed to read reservation for disk %s" % disk)
        if self.hostid in out:
            return True
        return False

    def disk_release(self, disk):
        self.set_read_only(0)
        cmd = [ 'sg_persist', '-n', '--out', '--release', '--param-rk='+self.hostid, '--prout-type='+self.prtype, disk ]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            self.log.error("failed to release disk %s" % disk)
        return ret

    def disk_clear_reservation(self, disk):
        cmd = [ 'sg_persist', '-n', '--out', '--clear', '--param-rk='+self.hostid, disk ]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            self.log.error("failed to clear reservation on disk %s" % disk)
        return ret

    def disk_reserve(self, disk):
        self.set_read_only(0)
        cmd = [ 'sg_persist', '-n', '--out', '--reserve', '--param-rk='+self.hostid, '--prout-type='+self.prtype, disk ]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            self.log.error("failed to reserve disk %s" % disk)
        return ret

    def _disk_preempt_reservation(self, disk, oldkey):
        m = __import__("rcDiskInfo"+rcEnv.sysname)
        if self.no_preempt_abort or m.diskInfo(deferred=True).disk_vendor(disk).strip() in ["VMware"]:
            preempt_opt = '--preempt'
        else:
            preempt_opt = '--preempt-abort'
        self.set_read_only(0)
        cmd = [ 'sg_persist', '-n', '--out', preempt_opt, '--param-sark='+oldkey, '--param-rk='+self.hostid, '--prout-type='+self.prtype, disk ]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            self.log.error("failed to preempt reservation for disk %s" % disk)
        return ret

  0707010005c9eb000081b40000000000000000000000015ab217540000004f000000660000000000000000000000000000003100000000root/usr/share/opensvc/lib/resScsiReservSunOS.py  import resScsiReservSg

class ScsiReserv(resScsiReservSg.ScsiReserv):
    pass
 0707010005c9ec000081b40000000000000000000000015ab2175400000e4e000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/resShareNfsHP-UX.py    import os

from rcGlobalEnv import rcEnv
from rcUtilities import justcall, which
import rcStatus
import rcExceptions as ex
from resources import Resource

class Share(Resource):

    def get_opts(self):
        if not os.path.exists(self.sharetab):
            return self.data
        with open(self.sharetab, 'r') as f:
            buff = f.read()
        for line in buff.split('\n'):
            words = line.split()
            if len(words) != 4:
                continue
            path = words[0]
            if path != self.path:
                continue
            res = words[1]
            fstype = words[2]
            if fstype != "nfs":
                continue
            opts = words[3]
            return self.parse_opts(opts)
        return ""

    def is_up(self):
        self.issues = ""
        opts = self.get_opts()
        if len(opts) == 0:
            return False
        if opts != self.opts:
            self.issues = "%s exported with unexpected options: %s, expected %s"%(self.path, opts, self.opts)
            return False
        return True

    def start(self):
        try:
            up = self.is_up()
        except ex.excError as e:
            self.log.error("skip start because the share is in unknown state")
            return
        if up:
            self.log.info("%s is already up" % self.path)
            return
        if "unexpected options" in self.issues:
            self.log.info("reshare %s because unexpected options were detected"%self.path)
            cmd = [ 'unshare', '-F', 'nfs', self.path ]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                raise ex.excError(err)
        self.can_rollback = True
        cmd = [ 'share', '-F', 'nfs', '-o', self.opts, self.path ]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError(err)

    def stop(self):
        try:
            up = self.is_up()
        except ex.excError as e:
            self.log.error("continue with stop even if the share is in unknown state")
        if not up:
            self.log.info("%s is already down" % self.path)
            return 0
        cmd = [ 'unshare', '-F', 'nfs', self.path ]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def _status(self, verbose=False):
        try:
            up = self.is_up()
        except ex.excError as e:
            self.status_log(str(e))
            return rcStatus.WARN
        if len(self.issues) > 0:
            self.status_log(self.issues)
            return rcStatus.WARN
        if up:
            return rcStatus.UP
        else:
            return rcStatus.DOWN

    def parse_opts(self, opts):
        o = sorted(opts.split(','))
        out = []
        for e in o:
            if e.startswith('ro=') or e.startswith('rw=') or e.startswith('access='):
                opt, clients = e.split('=')
                clients = ':'.join(sorted(clients.split(':')))
                if len(clients) == 0:
                    continue
                out.append('='.join((opt, clients)))
            else:
                out.append(e)
        return ','.join(out)

    def __init__(self, rid, path, opts, **kwargs):
        Resource.__init__(self, rid, type="share.nfs", **kwargs)
        self.sharetab = "/etc/dfs/sharetab"
        self.dfstab = "/etc/dfs/dfstab"

        if not which("share"):
            raise ex.excInitError("share is not installed")
        self.label = "nfs:"+path
        self.path = path
        try:
            self.opts = self.parse_opts(opts)
        except ex.excError as e:
            raise ex.excInitError(str(e))


  0707010005c9ed000081b40000000000000000000000015ab2175400001134000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/resShareNfsLinux.py    import os

from rcGlobalEnv import rcEnv
from rcUtilities import justcall, which
import rcStatus
import rcExceptions as ex
from resources import Resource

class Share(Resource):

    def get_exports(self):
        self.data = {}
        cmd = [ 'exportfs', '-v' ]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError(err)
        out = out.replace('\n ', '').replace('\n\t', '')
        for line in out.split('\n'):
            words = line.split()
            if len(words) != 2:
                continue
            path = words[0]
            e = words[1]
            if path not in self.data:
                self.data[path] = {}
            try:
                client, opts = self.parse_entry(e)
            except ex.excError as e:
                continue
            if client == '<world>':
                client = '*'
            self.data[path][client] = opts
        return self.data

    def is_up(self):
        self.issues = {}
        self.issues_missing_client = []
        self.issues_wrong_opts = []
        self.issues_none = []
        exports = self.get_exports()
        if self.path not in exports:
            return False
        for client in self.opts:
            if client not in exports[self.path]:
                self.issues[client] = "%s not exported to client %s"%(self.path, client)
                self.issues_missing_client.append(client)
            elif self.opts[client] > exports[self.path][client]:
                self.issues[client] = "%s is exported to client %s with missing options: current '%s', minimum required '%s'"%(self.path, client, ','.join(exports[self.path][client]), ','.join(self.opts[client]))
                self.issues_wrong_opts.append(client)
            else:
                self.issues_none.append(client)
        return True

    def start(self):
        try:
            up = self.is_up()
        except ex.excError as e:
            self.log.error("skip start because the share is in unknown state")
            return

        if up and len(self.issues) == 0:
            self.log.info("%s is already up" % self.path)
            return

        self.can_rollback = True
        for client, opts in self.opts.items():
            if client in self.issues_none:
                continue

            if client in self.issues_wrong_opts:
                cmd = [ 'exportfs', '-u', ':'.join((client, self.path)) ]
                ret, out, err = self.vcall(cmd)

            cmd = [ 'exportfs', '-o', ','.join(opts), ':'.join((client, self.path)) ]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                raise ex.excError

    def stop(self):
        try:
            up = self.is_up()
        except ex.excError as e:
            self.log.error("continue with stop even if the share is in unknown state")
        if not up:
            self.log.info("%s is already down" % self.path)
            return 0
        for client in self.opts:
            cmd = [ 'exportfs', '-u', ':'.join((client, self.path)) ]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                raise ex.excError

    def _status(self, verbose=False):
        try:
            up = self.is_up()
        except ex.excError as e:
            self.status_log(str(e))
            return rcStatus.WARN
        if len(self.issues) > 0:
            self.status_log('\n'.join(self.issues.values()))
            return rcStatus.WARN
        if up:
            return rcStatus.UP
        else:
            return rcStatus.DOWN

    def parse_entry(self, e):
        if '(' not in e or ')' not in e:
            raise ex.excError("malformed share opts: '%s'. must be in client(opts) client(opts) format"%e)
        _l = e.split('(')
        client = _l[0]
        opts = _l[1].strip(')')
        return client, set(opts.split(','))

    def __init__(self, rid, path, opts, **kwargs):
        Resource.__init__(self, rid, type="share.nfs", **kwargs)
        if not which("exportfs"):
            raise ex.excInitError("exportfs is not installed")
        self.label = "nfs:"+path
        self.path = path
        l = opts.replace('\\', '').split()
        self.opts = {}
        for e in l:
            try:
                client, opts = self.parse_entry(e)
            except ex.excError as e:
                raise ex.excInitError(str(e))
            self.opts[client] = opts


0707010005c9ee000081b40000000000000000000000015ab2175400000d72000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/resShareNfsSunOS.py    import os

from rcGlobalEnv import rcEnv
from rcUtilities import justcall, which
import rcStatus
import rcExceptions as ex
from resources import Resource

class Share(Resource):

    def get_opts(self):
        cmd = ["share", "-F", "nfs", "-A"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return self.data
        for line in out.splitlines():
            words = line.split()
            if len(words) != 3:
                continue
            path = words[1]
            if path != self.path:
                continue
            opts = words[2]
            return self.parse_opts(opts)
        return ""

    def is_up(self):
        self.issues = ""
        opts = self.get_opts()
        if len(opts) == 0:
            return False
        if opts != self.opts:
            self.issues = "%s exported with unexpected options: %s, expected %s"%(self.path, opts, self.opts)
            return False
        return True

    def start(self):
        try:
            up = self.is_up()
        except ex.excError as e:
            self.log.error("skip start because the share is in unknown state")
            return
        if up:
            self.log.info("%s is already up" % self.path)
            return
        if "unexpected options" in self.issues:
            self.log.info("reshare %s because unexpected options were detected"%self.path)
            cmd = ['unshare', '-F', 'nfs', self.path]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                raise ex.excError(err)
        self.can_rollback = True
        cmd = ['share', '-F', 'nfs', '-o', self.opts, self.path]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError(err)

    def stop(self):
        try:
            up = self.is_up()
        except ex.excError as e:
            self.log.error("continue with stop even if the share is in unknown state")
        if not up:
            self.log.info("%s is already down" % self.path)
            return 0
        cmd = ['unshare', '-F', 'nfs', self.path]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def _status(self, verbose=False):
        try:
            up = self.is_up()
        except ex.excError as e:
            self.status_log(str(e))
            return rcStatus.WARN
        if len(self.issues) > 0:
            self.status_log(self.issues)
            return rcStatus.WARN
        if up:
            return rcStatus.UP
        else:
            return rcStatus.DOWN

    def parse_opts(self, opts):
        o = sorted(opts.split(','))
        out = []
        for e in o:
            if e.startswith('ro=') or e.startswith('rw=') or e.startswith('access='):
                opt, clients = e.split('=')
                clients = ':'.join(sorted(clients.split(':')))
                if len(clients) == 0:
                    continue
                out.append('='.join((opt, clients)))
            else:
                out.append(e)
        return ','.join(out)

    def __init__(self, rid, path, opts, **kwargs):
        Resource.__init__(self, rid, type="share.nfs", **kwargs)

        if not which("share"):
            raise ex.excInitError("share is not installed")
        self.label = "nfs:"+path
        self.path = path
        try:
            self.opts = self.parse_opts(opts)
        except ex.excError as e:
            raise ex.excInitError(str(e))


  0707010005c9ef000081b40000000000000000000000015ab2175400001cbb000000660000000000000000000000000000002600000000root/usr/share/opensvc/lib/resSync.py import os
import logging
import json

import rcExceptions as ex
import resources as Res
import datetime
import time
import rcStatus
from rcGlobalEnv import rcEnv
from rcScheduler import *
from rcUtilities import lazy, bdecode
from converters import convert_speed, print_size

class Sync(Res.Resource, Scheduler):
    default_optional = True

    def __init__(self,
                 rid=None,
                 sync_max_delay=None,
                 schedule=None,
                 **kwargs):
        if sync_max_delay is None:
            self.sync_max_delay = 1500
        else:
            self.sync_max_delay = sync_max_delay

        if schedule is None:
            self.schedule = "03:59-05:59@121"
        else:
            self.schedule = schedule

        self.stats = {
            "bytes": 0,
            "speed": 0,
            "targets": {},
        }

        Res.Resource.__init__(self, rid=rid, **kwargs)

    def target_nodes(self, target):
        """
        Validate the target (either nodes or drpnodes), and return the
        corresponding set from the parent Svc object properties with the same
        name.
        """
        if target not in ("nodes", "drpnodes"):
            raise ex.excError("invalid target: %s" % target)
        return set([node for node in getattr(self.svc, target)])

    def can_sync(self, target):
        return True

    def check_timestamp(self, ts, comp='more', delay=10):
        """ Return False if timestamp is fresher than now-interval
            Return True otherwize.
            Zero is a infinite interval
        """
        if delay == 0:
            raise
        limit = ts + datetime.timedelta(minutes=delay)
        if comp == "more" and datetime.datetime.now() < limit:
            return False
        elif comp == "less" and datetime.datetime.now() < limit:
            return False
        else:
            return True
        return True

    def skip_sync(self, ts):
        if not self.svc.options.cron:
            return False
        if self.svc.sched.skip_action_schedule(self.rid, "sync_schedule", last=ts):
            return True
        return False

    def alert_sync(self, ts):
        if ts is None:
            return True
        if not self.check_timestamp(ts, comp="less", delay=self.sync_max_delay):
            return False
        return True

    def remote_fs_mounted(self, node):
        """
        Verify the remote fs is mounted. Some sync resource might want to abort in
        this case.
        """
        if self.dstfs is None:
            # No dstfs check has been configured. Assume the admin knows better.
            return True
        ruser = self.svc.node.get_ruser(node)
        cmd = rcEnv.rsh.split(' ')+['-l', ruser, node, '--', 'LANG=C', 'df', self.dstfs]
        (ret, out, err) = self.call(cmd, cache=True, errlog=False)
        if ret != 0:
            raise ex.excError

        """
        # df /zones
        /zones             (rpool/zones       ):131578197 blocks 131578197 files
               ^
               separator !

        # df /zones/frcp03vrc0108/root
        /zones/frcp03vrc0108/root(rpool/zones/frcp03vrc0108/rpool/ROOT/solaris-0):131578197 blocks 131578197 files
                                 ^
                                 no separator !
        """
        if self.dstfs+'(' not in out and self.dstfs not in out.split():
            self.log.error("The destination fs %s is not mounted on node %s. refuse to sync %s to protect parent fs"%(self.dstfs, node, self.dst))
            return False
        return True

    def pre_sync_check_svc_not_up(self):
        if self.svc.options.force:
            self.log.info("skip service up status check because --force is set")
        else:
            s = self.svc.group_status(excluded_groups=set(["sync", "app"]))
            if s['avail'].status != rcStatus.UP:
                if self.svc.options.cron:
                    self.log.debug("won't sync this resource for a service not up")
                else:
                    self.log.info("won't sync this resource for a service not up")
                raise ex.excAbortAction

    def pre_sync_check_flex_primary(self):
        """ Refuse to sync from a flex non-primary node
        """
        if self.svc.topology == "flex" and \
           self.svc.flex_primary != rcEnv.nodename:
            if self.svc.options.cron:
                self.log.debug("won't sync this resource from a flex non-primary node")
            else:
                self.log.info("won't sync this resource from a flex non-primary node")
            raise ex.excAbortAction

    def pre_sync_check_prd_svc_on_non_prd_node(self):
        if self.svc.svc_env == 'PRD' and rcEnv.node_env != 'PRD':
            if self.svc.options.cron:
                self.log.debug("won't sync a PRD service running on a !PRD node")
            else:
                self.log.info("won't sync a PRD service running on a !PRD node")
            raise ex.excAbortAction

    def _status(self, **kwargs):
        data = self.svc.node._daemon_status()
        try:
            svcnames = data["monitor"]["services"]
        except (KeyError, TypeError):
            # the daemon is not returning proper status data
            svcnames = []
        if self.svc.svcname in svcnames:
            avail = data["monitor"]["services"][self.svc.svcname]["avail"]
            if avail != "up":
                self.status_log("paused, service not up", "info")
                return rcStatus.NA
        return self.sync_status(**kwargs)

    @lazy
    def last_stats_file(self):
        return os.path.join(self.var_d, "last_stats")

    def parse_dd(self, buff):
        """
        Extract normalized speed and transfered data size from the dd output
        """
        data = {}
        words = bdecode(buff).split()
        if "bytes" in words:
            data["bytes"] = int(words[words.index("bytes")-1])
        if words[-1].endswith("/s"):
            data["speed"] = int(convert_speed("".join(words[-2:])))
        return data

    def update_stats(self, data, target=None):
        self.log.info("transfered %s at %s",
            print_size(data["bytes"], unit="B"),
            print_size(data["speed"], unit="B")+"/s"
        )
        # aggregate stats
        self.stats["bytes"] += data["bytes"]
        n_targets = len(self.stats["targets"])
        self.stats["speed"] = (data["speed"]*n_targets+data["speed"])/(n_targets+1)
        self.stats["targets"][target] = data

    def load_stats(self):
        try:
            with open(self.last_stats_file, "r") as ofile:
                return json.load(ofile)
        except Exception:
            return {}

    def write_stats(self):
        with open(self.last_stats_file, "w") as ofile:
            json.dump(self.stats, ofile)
        if len(self.stats["targets"]) < 2:
            return
        self.log.info("total transfered %s at %s to %d targets",
            print_size(self.stats["bytes"], unit="B"),
            print_size(self.stats["speed"], unit="B")+"/s",
            len(self.stats["targets"])
        )

    def stats_keys(self):
        stats = self.load_stats()
        data = []
        for key, val in stats.items():
            if not isinstance(val, (int, float)):
                continue
            data.append([key, str(val)])
        return data
 0707010005c9f0000081b40000000000000000000000015ab2175400003dc5000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/resSyncBtrfs.py    import os
from rcGlobalEnv import rcEnv
import datetime
from subprocess import *
import rcExceptions as ex
import rcStatus
import resSync
from rcUtilities import justcall
import rcBtrfs

class SyncBtrfs(resSync.Sync):
    """define btrfs sync resource to be btrfs send/btrfs receive between nodes
    """
    def sort_rset(self, rset):
        rset.resources.sort(key=lambda x: x.src_subvol)

    def __init__(self,
                 rid=None,
                 target=None,
                 src=None,
                 dst=None,
                 sender=None,
                 recursive=False,
                 snap_size=0,
                 **kwargs):
        resSync.Sync.__init__(self,
                              rid=rid,
                              type="sync.btrfs",
                              **kwargs)

        self.label = "btrfs of %s to %s"%(src, ", ".join(target))
        self.src = src
        self.target = target
        self.sender = sender
        self.recursive = recursive

        if ":" not in src or src.index(":") == len(src) - 1:
            raise ex.excInitError("malformed src value")
        if ":" not in dst or dst.index(":") == len(dst) - 1:
            raise ex.excInitError("malformed dst value")

        self.src_label = src[:src.index(":")]
        self.src_subvol = src[src.index(":")+1:]
        if dst is None:
            self.dst_label = self.src_label
            self.dst_subvol = self.src_subvol
        else:
            self.dst_label = dst[:dst.index(":")]
            self.dst_subvol = dst[dst.index(":")+1:]

        self.dst_btrfs = {}
        self.src_btrfs = None

    def _info(self):
        data = [
          ["src", self.src],
          ["target", " ".join(self.target) if self.target else ""],
        ]
        data += self.stats_keys()
        return data

    def init_src_btrfs(self):
        if self.src_btrfs is not None:
            return
        try:
            self.src_btrfs = rcBtrfs.Btrfs(label=self.src_label, resource=self)
        except rcBtrfs.ExecError as e:
            raise ex.excError(str(e))

    def pre_action(self, action):
        """Prepare snapshots
        Don't sync PRD services when running on !PRD node
        skip snapshot creation if delay_snap in tags
        delay_snap should be used for oracle archive datasets
        """
        resources = [r for r in self.rset.resources if \
                     not r.skip and not r.is_disabled() and \
                     r.type == self.type]

        if len(resources) == 0:
            return

        if not action.startswith('sync'):
            return

        self.pre_sync_check_svc_not_up()
        self.pre_sync_check_prd_svc_on_non_prd_node()

        self.init_src_btrfs()
        for i, r in enumerate(resources):
            if 'delay_snap' in r.tags:
                continue
            r.get_targets(action)
            tgts = r.targets.copy()
            if len(tgts) == 0:
                continue

            r.get_src_info()

            if not r.src_btrfs.has_subvol(r.src_snap_tosend):
                r.create_snap(r.src, r.src_snap_tosend)

    def __str__(self):
        return "%s target=%s src=%s" % (resSync.Sync.__str__(self),\
                self.target, self.src)

    def create_snap(self, snap_orig, snap):
        self.init_src_btrfs()
        try:
            self.src_btrfs.snapshot(snap_orig, snap, readonly=True, recursive=self.recursive)
        except rcBtrfs.ExistError:
            self.log.error('%s should not exist'%snap)
            raise ex.excError
        except rcBtrfs.ExecError:
            raise ex.excError

    def get_src_info(self):
        self.init_src_btrfs()
        subvol = self.src_subvol.replace('/','_')
        base = self.src_btrfs.snapdir + '/' + subvol
        self.src_snap_sent = base + '@sent'
        self.src_snap_tosend = base + '@tosend'
        self.src = os.path.join(self.src_btrfs.rootdir, self.src_subvol)

    def get_dst_info(self, node):
        if node not in self.dst_btrfs:
            try:
                self.dst_btrfs[node] = rcBtrfs.Btrfs(label=self.dst_label, resource=self, node=node)
            except rcBtrfs.ExecError as e:
                raise ex.excError(str(e))
            #self.dst_btrfs[node].setup_snap()
        subvol = self.src_subvol.replace('/','_')
        base = self.dst_btrfs[node].snapdir + '/' + subvol
        self.dst_snap_sent = base + '@sent'
        self.dst_snap_tosend = base + '@tosend'
        self.dst = os.path.join(self.dst_btrfs[node].rootdir, self.dst_subvol)

    def get_peersenders(self):
        self.peersenders = set()
        if 'nodes' == self.sender:
            self.peersenders |= self.svc.nodes
            self.peersenders -= set([rcEnv.nodename])

    def get_targets(self, action=None):
        self.targets = set()
        if 'nodes' in self.target and action in (None, 'sync_nodes'):
            self.targets |= self.svc.nodes
        if 'drpnodes' in self.target and action in (None, 'sync_drp'):
            self.targets |= self.svc.drpnodes
        self.targets -= set([rcEnv.nodename])

    def sync_nodes(self):
        self._sync_update('sync_nodes')

    def sync_drp(self):
        self._sync_update('sync_drp')

    def sanity_checks(self):
        self.pre_sync_check_svc_not_up()
        self.pre_sync_check_flex_primary()

    def sync_full(self):
        self.init_src_btrfs()
        try:
            self.sanity_checks()
        except ex.excError:
            return
        self.get_src_info()
        if not self.src_btrfs.has_subvol(self.src_snap_tosend):
            self.create_snap(self.src, self.src_snap_tosend)
        self.get_targets()
        for n in self.targets:
            self.get_dst_info(n)
            self.btrfs_send_initial(n)
            self.rotate_snaps(n)
            self.install_snaps(n)
        self.rotate_snaps()
        self.write_statefile()
        for n in self.targets:
            self.push_statefile(n)

    def btrfs_send_incremental(self, node):
        if self.recursive:
            send_cmd = ['btrfs', 'send', '-R',
                        '-c', self.src_snap_sent,
                        '-p', self.src_snap_sent,
                        self.src_snap_tosend]
        else:
            send_cmd = ['btrfs', 'send',
                        '-c', self.src_snap_sent,
                        '-p', self.src_snap_sent,
                        self.src_snap_tosend]

        receive_cmd = ['btrfs', 'receive', self.dst_btrfs[node].snapdir]
        if node is not None:
            receive_cmd = rcEnv.rsh.strip(' -n').split() + [node] + receive_cmd

        self.log.info(' '.join(send_cmd + ["|"] + receive_cmd))
        p1 = Popen(send_cmd, stdout=PIPE)
        pi = Popen(["dd", "bs=4096"], stdin=p1.stdout, stdout=PIPE, stderr=PIPE)
        p2 = Popen(receive_cmd, stdin=pi.stdout, stdout=PIPE)
        buff = p2.communicate()
        if p2.returncode == 0:
            stats_buff = pi.communicate()[1]
            stats = self.parse_dd(stats_buff)
            self.update_stats(stats, target=node)
        else:
            if buff[1] is not None and len(buff[1]) > 0:
                self.log.error(buff[1])
            self.log.error("sync update failed")
            raise ex.excError
        if buff[0] is not None and len(buff[0]) > 0:
            self.log.info(buff[0])

    def btrfs_send_initial(self, node=None):
        if self.recursive:
            send_cmd = ['btrfs', 'send', '-R', self.src_snap_tosend]
        else:
            send_cmd = ['btrfs', 'send', self.src_snap_tosend]

        receive_cmd = ['btrfs', 'receive', self.dst_btrfs[node].snapdir]
        if node is not None:
            receive_cmd = rcEnv.rsh.strip(' -n').split() + [node] + receive_cmd

        self.log.info(' '.join(send_cmd + ["|"] + receive_cmd))
        p1 = Popen(send_cmd, stdout=PIPE)
        pi = Popen(["dd", "bs=4096"], stdin=p1.stdout, stdout=PIPE, stderr=PIPE)
        p2 = Popen(receive_cmd, stdin=pi.stdout, stdout=PIPE)
        buff = p2.communicate()
        if p2.returncode == 0:
            stats_buff = pi.communicate()[1]
            stats = self.parse_dd(stats_buff)
            self.update_stats(stats, target=node)
        else:
            if buff[1] is not None and len(buff[1]) > 0:
                self.log.error(buff[1])
            self.log.error("full sync failed")
            raise ex.excError
        if buff[0] is not None and len(buff[0]) > 0:
            self.log.info(buff[0])

    def remove_snap_tosend(self, node=None):
        self.init_src_btrfs()
        if node is not None:
            o = self.dst_btrfs[node]
            subvol = self.dst_snap_tosend
        else:
            o = self.src_btrfs
            subvol = self.src_snap_tosend

        if not o.has_subvol(subvol):
            return

        try:
            o.subvol_delete(subvol, recursive=self.recursive)
        except rcBtrfs.ExecError:
            raise ex.excError()

    def remove_snap(self, node=None):
        self.init_src_btrfs()
        if node is not None:
            o = self.dst_btrfs[node]
            subvol = self.dst_snap_sent
        else:
            o = self.src_btrfs
            subvol = self.src_snap_sent

        if not o.has_subvol(subvol):
            return

        try:
            o.subvol_delete(subvol, recursive=self.recursive)
        except rcBtrfs.ExecError:
            raise ex.excError()

    def rename_snap(self, node=None):
        self.init_src_btrfs()
        if node is None:
            o = self.src_btrfs
            src = self.src_snap_tosend
            dst = self.src_snap_sent
        else:
            o = self.dst_btrfs[node]
            src = self.dst_snap_tosend
            dst = self.dst_snap_sent

        if o.has_subvol(dst):
            self.log.error("%s should not exist"%self.dst_snap_sent)
            raise ex.excError

        if self.recursive :
            # ??
            cmd = ['mv', src, dst]
        else:
            cmd = ['mv', src, dst]

        if node is not None:
            cmd = rcEnv.rsh.split() + [node] + cmd
        ret, out, err = self.vcall(cmd)

        if ret != 0:
            raise ex.excError

    def remove_dst(self, node=None):
        if node is None:
            return

        subvols = self.dst_btrfs[node].get_subvols_in_path(self.dst)

        try:
            self.dst_btrfs[node].subvol_delete(subvols)
        except rcBtrfs.ExecError:
            raise ex.excError()

    def install_dst(self, node=None):
        if node is None:
            return
        try:
            self.dst_btrfs[node].snapshot(self.dst_snap_sent, self.dst, readonly=False)
        except rcBtrfs.ExistError:
            self.log.error('%s should not exist'%snap)
            raise ex.excError()
        except rcBtrfs.ExecError:
            self.log.error("failed to install snapshot %s on node %s"%(self.dst, node))
            raise ex.excError()

    def install_snaps(self, node=None):
        self.remove_dst(node)
        self.install_dst(node)

    def rotate_snaps(self, node=None):
        self.remove_snap(node)
        self.rename_snap(node)

    def _sync_update(self, action):
        self.init_src_btrfs()
        try:
            self.sanity_checks()
        except ex.excError:
            return
        self.get_targets(action)
        if len(self.targets) == 0:
            return
        self.get_src_info()

        if not self.src_btrfs.has_subvol(self.src_snap_tosend):
            self.create_snap(self.src, self.src_snap_tosend)

        for n in self.targets:
            self.get_dst_info(n)
            self.remove_snap_tosend(n)
            if self.src_btrfs.has_subvol(self.src_snap_sent) and self.dst_btrfs[n].has_subvol(self.dst_snap_sent):
                self.btrfs_send_incremental(n)
            else:
                self.btrfs_send_initial(n)
            self.rotate_snaps(n)
            self.install_snaps(n)

        self.rotate_snaps()
        self.write_statefile()
        for n in self.targets:
            self.push_statefile(n)
        self.write_stats()

    def start(self):
        pass

    def stop(self):
        pass

    def can_sync(self, target=None):
        try:
            ls = self.get_local_state()
            ts = datetime.datetime.strptime(ls['date'], "%Y-%m-%d %H:%M:%S.%f")
        except IOError:
            self.log.error("btrfs state file not found")
            return True
        except:
            import sys
            import traceback
            e = sys.exc_info()
            print(e[0], e[1], traceback.print_tb(e[2]))
            return False
        if self.skip_sync(ts):
            self.status_log("Last sync on %s older than %i minutes"%(ts, self.sync_max_delay))
            return False
        return True

    def sync_status(self, verbose=False):
        self.init_src_btrfs()
        try:
            ls = self.get_local_state()
            now = datetime.datetime.now()
            last = datetime.datetime.strptime(ls['date'], "%Y-%m-%d %H:%M:%S.%f")
            delay = datetime.timedelta(minutes=self.sync_max_delay)
        except IOError:
            self.status_log("btrfs state file not found")
            return rcStatus.WARN
        except:
            import sys
            import traceback
            e = sys.exc_info()
            print(e[0], e[1], traceback.print_tb(e[2]))
            return rcStatus.WARN
        if last < now - delay:
            self.status_log("Last sync on %s older than %i minutes"%(last, self.sync_max_delay))
            return rcStatus.WARN
        return rcStatus.UP

    def check_remote(self, node):
        rs = self.get_remote_state(node)
        if self.snap1_uuid != rs['uuid']:
            self.log.error("%s last update uuid doesn't match snap1 uuid"%(node))
            raise ex.excError

    def get_remote_state(self, node):
        self.set_statefile()
        cmd1 = ['cat', self.statefile]
        cmd = rcEnv.rsh.split() + [node] + cmd1
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            self.log.error("could not fetch %s last update uuid"%node)
            raise ex.excError
        return self.parse_statefile(out, node=node)

    def get_local_state(self):
        self.set_statefile()
        with open(self.statefile, 'r') as f:
            out = f.read()
        return self.parse_statefile(out)

    def get_snap_uuid(self, snap):
        self.init_src_btrfs()
        self.snap_uuid = self.src_btrfs.get_transid(snap)

    def set_statefile(self):
        self.statefile = os.path.join(self.var_d, 'btrfs_state')

    def write_statefile(self):
        self.set_statefile()
        self.get_snap_uuid(self.src_snap_sent)
        self.log.info("update state file with snap uuid %s"%self.snap_uuid)
        with open(self.statefile, 'w') as f:
             f.write(str(datetime.datetime.now())+';'+self.snap_uuid+'\n')

    def _push_statefile(self, node):
        cmd = rcEnv.rcp.split() + [self.statefile, node+':'+self.statefile.replace('#', '\#')]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def push_statefile(self, node):
        self.set_statefile()
        self._push_statefile(node)
        self.get_peersenders()
        for s in self.peersenders:
            self._push_statefile(s)

    def parse_statefile(self, out, node=None):
        self.set_statefile()
        if node is None:
            node = rcEnv.nodename
        lines = out.strip().split('\n')
        if len(lines) != 1:
            self.log.error("%s:%s is corrupted"%(node, self.statefile))
            raise ex.excError
        fields = lines[0].split(';')
        if len(fields) != 2:
            self.log.error("%s:%s is corrupted"%(node, self.statefile))
            raise ex.excError
        return dict(date=fields[0], uuid=fields[1])

   0707010005c9f2000081b40000000000000000000000015ab2175400001086000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/resSyncDcs.py  import os
import rcExceptions as ex
import subprocess
import resSync
from rcGlobalEnv import rcEnv
from rcUtilities import justcall

class SyncDcs(resSync.Sync):
    def wait_for_devs_ready(self):
        pass

    def get_active_dcs(self):
        if self.active_dcs is not None:
            return
        for d in self.dcs:
            try:
                self.log.debug("try dcs", d)
                self.dcscmd("get-dcsserver", dcs=d)
                self.active_dcs = d
                self.log.debug("set active dcs", self.active_dcs)
                return
            except:
                pass
        if self.active_dcs is None:
            self.log.error("no usable dcs server")
            raise ex.excError

    def get_auth(self):
        if self.username is not None and \
           self.password is not None:
            return
        self.get_active_manager()
        if self.active_manager is None:
            raise ex.excError("no active manager")
        import ConfigParser
        if not os.path.exists(self.conf):
            raise ex.excError("missing %s"%self.conf)
        self.config = ConfigParser.RawConfigParser()
        self.config.read(self.conf)
        if not self.config.has_section(self.active_manager):
            raise ex.excError("no credentials for manager %s in %s"%(self.active_manager, self.conf))
        if not self.config.has_option(self.active_manager, "username"):
            raise ex.excError("no username set for manager %s in %s"%(self.active_manager, self.conf))
        if not self.config.has_option(self.active_manager, "password"):
            raise ex.excError("no password set for manager %s in %s"%(self.active_manager, self.conf))
        self.username = self.config.get(self.active_manager, "username")
        self.password = self.config.get(self.active_manager, "password")

    def dcscmd(self, cmd="", verbose=False, check=True, dcs=None):
        if len(cmd) == 0:
            return

        self.get_active_manager()
        if dcs is None:
            self.get_active_dcs()
            dcs = self.active_dcs
        self.get_auth()
        cmd = self.ssh + [self.active_manager,
               "connect-dcsserver -server %s -username %s -password %s -connection %s ; "%(dcs, self.username, self.password, self.conn)+\
               cmd+\
               " ; disconnect-dcsserver -connection %s"%self.conn]
        if verbose:
            import re
            from copy import copy
            _cmd = copy(cmd)
            _cmd[2] = re.sub(r'password \S+', 'password xxxxx', _cmd[2])
            self.log.info(subprocess.list2cmdline(_cmd))
            ret, out, err = self.call(cmd)
        else:
            ret, out, err = self.call(cmd, errlog=False)
        if check and "ErrorId" in err:
            raise ex.excError("dcscmd command execution error")
        return ret, out, err

    def get_active_manager(self):
        if self.active_manager is not None:
            return
        for manager in self.manager:
            cmd = self.ssh + [manager, 'id']
            out, err, ret = justcall(cmd)
            if ret != 0:
                continue
            self.active_manager = manager
            self.log.debug("set active manager", self.active_manager)
            return
        if self.active_manager is None:
            self.log.error("no usable manager")
            raise ex.excError

    def __init__(self, rid=None, manager=set(), dcs=set(), **kwargs):
        resSync.Sync.__init__(self, rid=rid, **kwargs)
        self.ssh = rcEnv.rsh.split()
        self.active_dcs = None
        self.active_manager = None
        self.username = None
        self.password = None
        self.dcs = dcs
        self.manager = manager
        self.conf = os.path.join(rcEnv.paths.pathetc, 'auth.conf')

    def on_add(self):
        self.get_conn()

    def get_conn(self):
        from hashlib import md5
        import uuid
        o = md5()
        o.update(uuid.uuid1().hex)
        o.update(self.svc.svcname)
        self.conn = o.hexdigest()

    def __str__(self):
        return "%s dcs=%s manager=%s" % (
                 resSync.Sync.__str__(self),
                 ' '.join(self.dcs),
                 ' '.join(self.manager))

  0707010005c9f1000081b40000000000000000000000015ab2175400001a4a000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/resSyncBtrfsSnap.py    import os

from rcGlobalEnv import rcEnv
import rcExceptions as ex
import rcStatus
import time
import datetime
import resSync
import rcBtrfs
from rcUtilities import justcall

class syncBtrfsSnap(resSync.Sync):
    def __init__(self,
                 rid=None,
                 name=None,
                 subvol=[],
                 keep=1,
                 **kwargs):
        resSync.Sync.__init__(self,
                              rid=rid,
                              type="sync.btrfssnap",
                              **kwargs)

        if name:
            self.label = "btrfs '%s' snapshot %s" % (name, ", ".join(subvol))
        else:
            self.label = "btrfs snapshot %s" % ", ".join(subvol)
        self.subvol = subvol
        self.keep = keep
        self.name = name
        self.btrfs = {}

    def on_add(self):
        pass

    def test_btrfs(self, label):
        cmd = [rcEnv.syspaths.blkid, "-L", label]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return ret
        dev = out.strip()
        dev = os.path.realpath(dev)
        import glob
        holders = glob.glob("/sys/block/%s/holders/*" % os.path.basename(dev))
        if len(holders) == 1:
            hdev = "/dev/%s" % os.path.basename(holders[0])
        else:
            hdev = None
        if hdev and os.path.exists(hdev):
            dev = hdev
        cmd = ["btrfs", "fi", "show", dev]
        out, err, ret = justcall(cmd)
        return ret

    def stop(self):
        for s in self.subvol:
            try:
                label, subvol = s.split(":")
            except:
                self.log.error("misformatted subvol entry %s (expected <label>:<subvol>)" % s)
                continue
            btrfs = self.get_btrfs(label)
            cmd = ["umount", btrfs.rootdir]
            self.vcall(cmd)

    def get_btrfs(self, label):
        if label in self.btrfs:
            return self.btrfs[label]
        try:
            self.btrfs[label] = rcBtrfs.Btrfs(label=label, resource=self)
        except rcBtrfs.ExecError as e:
            raise ex.excError(str(e))
        return self.btrfs[label]

    def create_snap(self, label, subvol):
        btrfs = self.get_btrfs(label)
        orig = os.path.join(btrfs.rootdir, subvol)
        snap = os.path.join(btrfs.rootdir, subvol)
        if self.name:
            suffix = "."+self.name
        else:
            suffix = ""
        suffix += ".snap.%Y-%m-%d.%H:%M:%S"
        snap += datetime.datetime.now().strftime(suffix)
        try:
            btrfs.snapshot(orig, snap, readonly=True, recursive=False)
        except rcBtrfs.ExistError:
            raise ex.excError('%s should not exist'%snap)
        except rcBtrfs.ExecError:
            raise ex.excError

    def remove_snap(self, label, subvol):
        btrfs = self.get_btrfs(label)
        btrfs.get_subvols(refresh=True)
        snaps = {}
        for sv in btrfs.subvols.values():
            if not sv["path"].startswith(subvol):
                continue
            s = sv["path"].replace(subvol, "")
            l = s.split('.')
            if len(l) < 2:
                continue
            if l[1] not in ("snap", self.name):
                continue
            try:
                ds = sv["path"].split(".snap.")[-1]
                d = datetime.datetime.strptime(ds, "%Y-%m-%d.%H:%M:%S")
                snaps[ds] = sv["path"]
            except Exception as e:
                pass
        if len(snaps) <= self.keep:
            return
        sorted_snaps = []
        for ds in sorted(snaps.keys(), reverse=True):
            sorted_snaps.append(snaps[ds])
        for path in sorted_snaps[self.keep:]:
            try:
                btrfs.subvol_delete(os.path.join(btrfs.rootdir, path), recursive=False)
            except rcBtrfs.ExecError:
                raise ex.excError

    def _status_one(self, label, subvol):
        if self.test_btrfs(label) != 0:
            self.status_log("snap of %s suspended: not writable"%label, "info")
            return
        try:
            btrfs = self.get_btrfs(label)
        except Exception as e:
            self.status_log("%s:%s %s" % (label, subvol, str(e)))
            return
        try:
            btrfs.get_subvols()
        except:
            return
        snaps = []
        for sv in btrfs.subvols.values():
            if not sv["path"].startswith(subvol):
                continue
            s = sv["path"].replace(subvol, "")
            l = s.split('.')
            if len(l) < 2:
                continue
            if l[1] not in ("snap", self.name):
                continue
            try:
                ds = sv["path"].split(".snap.")[-1]
                d = datetime.datetime.strptime(ds, "%Y-%m-%d.%H:%M:%S")
                snaps.append(d)
            except Exception as e:
                pass
        if len(snaps) == 0:
            self.status_log("%s:%s has no snap" % (label, subvol))
            return
        if len(snaps) > self.keep:
            self.status_log("%s:%s has %d too many snaps" % (label, subvol, len(snaps)-self.keep))
        last = sorted(snaps, reverse=True)[0]
        limit = datetime.datetime.now() - datetime.timedelta(minutes=self.sync_max_delay)
        if last < limit:
            self.status_log("%s:%s last snap is too old (%s)" % (label, subvol, last.strftime("%Y-%m-%d %H:%M:%S")))

    def _status(self, verbose=False):
        for s in self.subvol:
            try:
                label, subvol = s.split(":")
            except:
                self.status_log("misformatted subvol entry %s (expected <label>:<subvol>)" % s)
                continue
            self._status_one(label, subvol)
        messages = set(self.status_logs_get(["warn"])) - set([''])
        not_writable = set([r for r in messages if "not writable" in r])
        issues = messages - not_writable

        if len(not_writable) > 0 and len(not_writable) == len(messages):
            return rcStatus.NA
        if len(issues) == 0:
            return rcStatus.UP
        return rcStatus.WARN

    def _sync_update(self, s):
        try:
            label, subvol = s.split(":")
        except:
            self.log.error("misformatted subvol entry %s (expected <label>:<subvol>)" % s)
            return
        if self.test_btrfs(label) != 0:
            self.log.info("skip snap of %s while the btrfs is no writable"%label)
            return
        self.create_snap(label, subvol)
        self.remove_snap(label, subvol)

    def sync_update(self):
        for subvol in self.subvol:
            self._sync_update(subvol)

    def __str__(self):
        return "%s subvol=%s keep=%s" % (resSync.Sync.__str__(self), str(self.subvol), str(self.keep))

  0707010005c9f3000081b40000000000000000000000015ab2175400001a57000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/resSyncDcsCkpt.py  import os
import rcExceptions as ex
import rcStatus
import datetime
import resSyncDcs
from rcGlobalEnv import rcEnv
from rcUtilities import justcall

class syncDcsCkpt(resSyncDcs.SyncDcs):
    def can_sync(self, target=None):
        return True

    def checkpoint(self):
        cmd = ""
        vars = ""
        for i, d in enumerate(self.pairs):
            cmd += '$v%d=Get-DcsVirtualDisk -VirtualDisk %s -connection %s;'%(i, d['src'], self.conn)
            vars += '$v%d '%i

        cmd += "echo %s|Set-DcsReplicationCheckPoint -connection %s"%(vars, self.conn)
        self.dcscmd(cmd, verbose=True)
        self.update_tsfile()

    def get_snap(self, snap):
        if snap in self._info:
            return self._info[snap]

        cmd = 'get-dcssnapshot -snapshot %s -connection %s;'%(snap, self.conn)
        try:
            ret, out, err = self.dcscmd(cmd)
        except:
            return None

        info = {}
        lines = out.split('\n')
        for line in lines:
            l = line.split(': ')
            if len(l) != 2:
                continue
            var = l[0].strip()
            val = l[1].strip()
            if var == 'TimeStamp':
                info['TimeStamp'] = datetime.datetime.strptime(val, "%d/%m/%Y %H:%M:%S")
            elif var in ['Type', 'State', 'ActiveOperation', 'Failure']:
                info[var] = val
        if len(info) > 0:
            self._info[snap] = info
        return info

    def drpnodes_status(self, verbose=False, skip_prereq=False):
        err = False
        errlog = []
        log = []
        try:
            self.get_auth()
        except ex.excError as e:
            self.status_log(str(e))
            return rcStatus.WARN
        for snap in [p['dst_ckpt'] for p in self.pairs]:
            info = self.get_snap(snap)
            if info is None:
                errlog.append("checkpoint snapshot %s does not exists"%snap)
                err |= True
                continue
            if info['State'] not in ['Healthy']:
                errlog.append("checkpoint snapshot %s state is %s"%(snap, info['State']))
                err |= True
            if info['Failure'] not in ['NoFailure']:
                errlog.append("checkpoint snapshot %s failure state is %s"%(snap, info['Failure']))
                err |= True
            if info['TimeStamp'] < datetime.datetime.now() - datetime.timedelta(minutes=self.sync_max_delay):
                errlog.append("checkpoint snapshot %s too old"%snap)
                err |= True
            log.append("last update on %s"%info['TimeStamp'])
        if err:
            self.status_log('\n'.join(errlog))
            return rcStatus.WARN
        self.status_log('\n'.join(log))
        return rcStatus.UP

    def nodes_status(self, verbose=False, skip_prereq=False):
        err = False
        ts = self.read_tsfile()
        if ts < datetime.datetime.now() - datetime.timedelta(minutes=self.sync_max_delay):
            self.status_log("checkpoint too old")
            err |= True
        self.status_log("last update on %s"%str(ts))
        if err:
            return rcStatus.WARN
        return rcStatus.UP

    def sync_status(self, verbose=False, skip_prereq=False):
        if rcEnv.nodename in self.svc.nodes:
            return self.nodes_status(verbose, skip_prereq)
        else:
            return self.drpnodes_status(verbose, skip_prereq)

    def pause_checkpoint(self):
        cmd = ""
        for d in self.pairs:
            cmd += 'Disable-DcsTask -Task %s -connection %s ; '%(self.task_name(d['dst_ckpt']), self.conn)
        self.dcscmd(cmd, verbose=True)

    def create_task(self):
        cmd = "Get-DcsTask -connection %s"%self.conn
        tasks = []
        ret, out, err = self.dcscmd(cmd, verbose=False)
        for line in out.split('\n'):
             if line.startswith("Caption"):
                 tasks.append(line.split(':')[-1].strip())

        cmd = ""
        for d in self.pairs:
            if self.task_name(d['dst_ckpt']) in tasks:
                continue
            cmd += "Add-DcsTask -Disabled -Name %s -connection %s; "%(self.task_name(d['dst_ckpt']), self.conn)
            cmd += 'Add-DcsTrigger -Task %s -VirtualDisk "%s" -connection %s; '%(self.task_name(d['dst_ckpt']), d['dst'], self.conn)
            cmd += 'Add-DcsAction -Task %s -MethodActionType UpdateSnapshot -connection %s -TargetId (Get-DcsSnapshot -snapshot "%s" -connection %s).Id ; '%(self.task_name(d['dst_ckpt']), self.conn, d['dst_ckpt'], self.conn)
        self.dcscmd(cmd, verbose=True)

    def resume_checkpoint(self):
        self.create_task()
        cmd = ""
        for d in self.pairs:
            cmd += 'Enable-DcsTask -Task %s -connection %s; '%(self.task_name(d['dst_ckpt']), self.conn)
        self.dcscmd(cmd, verbose=True)

    def task_name(self, id):
        return '-'.join((self.svc.svcname, self.rid, id))

    def sync_break(self):
        self.pause_checkpoint()

    def start(self):
        if rcEnv.nodename not in self.svc.drpnodes:
            return
        self.pause_checkpoint()

    def sync_resume(self):
        self.resume_checkpoint()

    def sync_update(self):
        self.checkpoint()

    def refresh_svcstatus(self):
        self.svcstatus = self.svc.group_status(excluded_groups=set(["sync", 'hb', 'app']))

    def get_svcstatus(self):
        if len(self.svcstatus) == 0:
            self.refresh_svcstatus()

    def __init__(self,
                 rid=None,
                 pairs=[],
                 manager=set(),
                 dcs=set(),
                 **kwargs):
        resSyncDcs.SyncDcs.__init__(self,
                                    rid=rid, type="sync.dcsckpt",
                                    manager=manager,
                                    dcs=dcs,
                                    **kwargs)

        self.label = "DCS checkpoint snapshot of %s"%' ,'.join(map(lambda x: x['src'], pairs))
        self.pairs = pairs
        self._info = {}

    def tsfile(self):
        return os.path.join(self.var_d, 'ts')

    def update_tsfile(self):
        import datetime
        now = datetime.datetime.now()
        with open(self.tsfile(), 'w') as f:
            f.write(str(now)+'\n')

    def read_tsfile(self):
        import datetime
        try:
            with open(self.tsfile(), 'r') as f:
                ts = datetime.datetime.strptime(f.read(),"%Y-%m-%d %H:%M:%S.%f\n")
        except:
            ts = datetime.datetime.fromtimestamp(0)
        return ts

    def __str__(self):
        return "%s dcs=%s manager=%s pairs=%s" % (
                 resSync.Sync.__str__(self),
                 ' '.join(self.dcs),
                 ' '.join(self.manager),
                 str(pairs))

 0707010005c9f4000081b40000000000000000000000015ab2175400001858000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/resSyncDcsSnap.py  import os
import rcExceptions as ex
import rcStatus
import datetime
import resSyncDcs
from rcGlobalEnv import rcEnv
from rcUtilities import justcall

class syncDcsSnap(resSyncDcs.SyncDcs):
    def can_sync(self, target=None):
        ts = None

        """ get oldest snap
        """
        for snap in self.snapname:
            info = self.get_snap(snap)
            if info is None:
                self.log.debug("snap %s missing"%snap)
                return True
            _ts = info['TimeStamp']
            if ts is None or _ts < ts:
                ts = _ts
        return not self.skip_sync(ts)

    def update_snap(self):
        cmd = ""
        vars = ""
        for i, snap in enumerate(self.snapname):
            cmd += '$v%d=get-dcssnapshot -snapshot %s -connection %s;'%(i, snap, self.conn)
            vars += '$v%d '%i

        cmd += "echo %s|update-dcssnapshot -Y -connection %s"%(vars, self.conn)
        self.dcscmd(cmd, verbose=True)

    def get_snaps(self):
        cmd = ""
        for i, snap in enumerate(self.snapname):
            cmd += 'get-dcssnapshot -snapshot %s -connection %s;'%(snap, self.conn)
        try:
            ret, out, err = self.dcscmd(cmd)
        except:
            return

        """
SourceLogicalDiskId      : ef989edf-4a6d-4af6-8b9d-bc6d2070a36c
DestinationLogicalDiskId : 24a6a20f-59f3-4c4d-b4de-9a4f45afff44
Type                     : Full
TimeStamp                : 17/01/2013 14:16:49
ActiveOperation          : NoOperation
State                    : Migrated
Failure                  : NoFailure
SequenceNumber           : 4677814244
Id                       : V.{06C86883-CF53-11E1-9203-441EA14CCCC6}-00000177--V
                           .{06C86883-CF53-11E1-9203-441EA14CCCC6}-000001D7
Caption                  : S64lmwbic6f-22-clone-02
ExtendedCaption          : S64lmwbic6f-22-clone-02 on SDSLMW03
Internal                 : False

SourceLogicalDiskId      : f0450ff7-076f-4dbc-bee0-25f6a586f5b2
DestinationLogicalDiskId : 17e98dbf-1457-41c9-aaad-5b6d7d8cc81c
Type                     : Full
TimeStamp                : 17/01/2013 14:16:49
ActiveOperation          : NoOperation
State                    : Migrated
Failure                  : NoFailure
SequenceNumber           : 4677814247
Id                       : V.{06C86883-CF53-11E1-9203-441EA14CCCC6}-0000017A--V
                           .{06C86883-CF53-11E1-9203-441EA14CCCC6}-000001DA
Caption                  : S64lmwbic6f-25-clone-02
ExtendedCaption          : S64lmwbic6f-25-clone-02 on SDSLMW03
Internal                 : False

"""
        info = {}
        lines = out.split('\n')
        for line in lines:
            l = line.split(': ')
            if len(l) != 2:
                continue
            var = l[0].strip()
            val = l[1].strip()
            if var == 'Internal' and len(info) > 0:
                self._info[info['Caption']] = info
                info = {}
            if var == 'TimeStamp':
                info['TimeStamp'] = datetime.datetime.strptime(val, "%d/%m/%Y %H:%M:%S")
            elif var in ['Type', 'State', 'ActiveOperation', 'Failure', 'Caption']:
                info[var] = val

    def get_snap(self, snap):
        if len(self._info) == 0:
            self.get_snaps()
        if snap not in self._info:
            return None
        return self._info[snap]

    def no_status(self):
        try:
            self.pre_sync_check_flex_primary()
        except ex.excAbortAction:
            return True
        s = self.svc.group_status(excluded_groups=set(["sync", "hb", "app"]))
        if s['overall'].status not in [rcStatus.UP, rcStatus.NA]:
            return True
        return False

    def _status(self, verbose=False, skip_prereq=False):
        if self.no_status():
            self.status_log("skip on secondary node")
            return rcStatus.NA

        err = False
        errlog = []
        log = []
        try:
            self.get_auth()
        except ex.excError as e:
            self.status_log(str(e))
            return rcStatus.WARN
        for snap in self.snapname:
            info = self.get_snap(snap)
            if info is None:
                errlog.append("snapshot %s does not exists"%snap)
                err |= True
                continue
            if info['State'] not in ['Healthy','Migrated']:
                errlog.append("snapshot %s state is %s"%(snap, info['State']))
                err |= True
            if info['Failure'] not in ['NoFailure']:
                errlog.append("snapshot %s failure state is %s"%(snap, info['Failure']))
                err |= True
            if info['TimeStamp'] < datetime.datetime.now() - datetime.timedelta(minutes=self.sync_max_delay):
                errlog.append("snapshot %s too old"%snap)
                err |= True
            log.append("last update on %s"%info['TimeStamp'])
        if err:
            self.status_log('\n'.join(errlog))
            return rcStatus.WARN
        self.status_log('\n'.join(log))
        return rcStatus.UP

    def sync_resync(self):
        self.update_snap()

    def sync_update(self):
        self.update_snap()

    def refresh_svcstatus(self):
        self.svcstatus = self.svc.group_status(excluded_groups=set(["sync", 'hb', 'app']))

    def get_svcstatus(self):
        if len(self.svcstatus) == 0:
            self.refresh_svcstatus()

    def __init__(self,
                 rid=None,
                 snapname=set(),
                 manager=set(),
                 dcs=set(),
                 **kwargs):
        resSyncDcs.SyncDcs.__init__(self,
                                    rid=rid,
                                    type="sync.dcssnap",
                                    manager=manager,
                                    dcs=dcs,
                                    **kwargs)

        self.label = "DCS snapshot %s"%', '.join(snapname)
        self.snapname = snapname
        self._info = {}
        self.default_schedule = "@0"

    def __str__(self):
        return "%s dcs=%s manager=%s snapname=%s" % (
                 resSync.Sync.__str__(self),
                 ' '.join(self.dcs),
                 ' '.join(self.manager),
                 ' '.join(self.snapname))

0707010005c9f5000081b40000000000000000000000015ab2175400003601000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/resSyncDds.py  import os
import logging

from rcGlobalEnv import rcEnv
from rcUtilities import which
from rcUtilitiesLinux import lv_info
from subprocess import *
import rcExceptions as ex
import rcStatus
import datetime
import resSync

class syncDds(resSync.Sync):
    def pre_action(self, action):
        resources = [r for r in self.rset.resources if \
                     not r.skip and not r.is_disabled() and \
                     r.type == self.type]

        if len(resources) == 0:
            return

        self.pre_sync_check_prd_svc_on_non_prd_node()

        for i, r in enumerate(resources):
            if not r.svc_syncable():
                return
            r.get_info()
            if action == 'sync_full':
                r.create_snap1()
            elif action in ['sync_update', 'sync_resync', 'sync_drp', 'sync_nodes']:
                if action == 'sync_nodes' and self.target != ['nodes']:
                    return
                if action == 'sync_drp' and self.target != ['drpnodes']:
                    return
                r.get_info()
                r.get_snap1_uuid()
                nb = 0
                tgts = r.targets.copy()
                for n in tgts:
                    try:
                        r.check_remote(n)
                        nb += 1
                    except:
                        self.targets -= set([n])
                if nb != len(tgts):
                    self.log.error('all destination nodes must be present for dds-based synchronization to proceed')
                    raise ex.excError
                r.create_snap2()

    def snap_exists(self, dev):
        if not os.path.exists(dev):
            self.log.debug('dev path does not exist')
            return False
        cmd = [rcEnv.syspaths.lvs, '--noheadings', '-o', 'snap_percent', dev]
        (ret, out, err) = self.call(cmd, errlog=False)
        if ret != 0:
            return False
        if len(out.strip()) == 0:
            self.log.debug('dev is not a snapshot')
            return False
        return True

    def create_snap(self, dev, lv):
        if self.snap_exists(dev):
            self.log.error('%s should not exist'%dev)
            raise ex.excError
        cmd = ['lvcreate', '-s', '-n', lv,
               '-L', str(self.snap_size)+'M',
               os.path.join(os.sep, 'dev', self.src_vg, self.src_lv)
              ]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def set_statefile(self):
        self.statefile = os.path.join(self.var_d, 'dds_state')

    def create_snap1(self):
        if self.snap_exists(self.snap2):
            self.log.error('%s should not exist'%self.snap2)
            raise ex.excError
        self.create_snap(self.snap1, self.snap1_lv)
        self.write_statefile()

    def create_snap2(self):
        self.create_snap(self.snap2, self.snap2_lv)

    def snap_name(self, snap):
        return os.path.basename(self.src_lv).replace('-', '_')+'_osvc_'+snap

    def get_src_info(self):
        (self.src_vg, self.src_lv, self.src_size) = lv_info(self, self.src)
        if self.src_lv is None:
            self.log.error("unable to fetch source logical volume information")
            raise ex.excError
        if self.snap_size == 0:
            self.snap_size = self.src_size//10
        self.snap1_lv = self.snap_name('snap1')
        self.snap2_lv = self.snap_name('snap2')
        self.snap1 = os.path.join(os.sep, 'dev', self.src_vg, self.snap1_lv)
        self.snap2 = os.path.join(os.sep, 'dev', self.src_vg, self.snap2_lv)
        self.snap1_cow = os.path.join(os.sep, 'dev', 'mapper',
                                      '-'.join([self.src_vg.replace('-', '--'),
                                                self.snap1_lv,
                                                'cow'])
                                     )

    def get_peersenders(self):
        self.peersenders = set()
        if 'nodes' not in self.target:
            self.peersenders |= self.svc.nodes
            self.peersenders -= set([rcEnv.nodename])

    def get_targets(self):
        self.targets = set()
        if 'nodes' in self.target:
            self.targets |= self.svc.nodes
        if 'drpnodes' in self.target:
            self.targets |= self.svc.drpnodes
        self.targets -= set([rcEnv.nodename])

    def get_info(self):
        self.get_targets()
        self.get_src_info()

    def svc_syncable(self):
        try:
            self.pre_sync_check_svc_not_up()
            self.pre_sync_check_flex_primary()
        except ex.excAbortAction:
            return False
        return True

    def sync_full(self):
        if not self.svc_syncable():
            return
        for n in self.targets:
            self.do_fullsync(n)

    def do_fullsync(self, node):
        dst = self.dsts[node]
        cmd1 = ['dd', 'if='+self.snap1, 'bs=1M']
        cmd2 = rcEnv.rsh.split() + [node, 'dd', 'bs=1M', 'of='+dst]
        self.log.info(' '.join(cmd1 + ["|"] + cmd2))
        p1 = Popen(cmd1, stdout=PIPE)
        p2 = Popen(cmd2, stdin=p1.stdout, stdout=PIPE)
        stats_buff = p2.communicate()[1]
        stats = self.parse_dd(stats_buff)
        self.update_stats(stats, target=node)
        if p2.returncode != 0:
            self.log.error("full sync failed")
            raise ex.excError
        self.push_statefile(node)

    def get_snap1_uuid(self):
        cmd = [rcEnv.syspaths.lvs, '--noheadings', '-o', 'uuid', self.snap1]
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            raise ex.excError
        self.snap1_uuid = out.strip()

    def write_statefile(self):
        self.set_statefile()
        self.get_snap1_uuid()
        self.log.info("update state file with snap uuid %s"%self.snap1_uuid)
        with open(self.statefile, 'w') as f:
             f.write(str(datetime.datetime.now())+';'+self.snap1_uuid+'\n')

    def _push_statefile(self, node):
        cmd = rcEnv.rcp.split() + [self.statefile, node+':'+self.statefile]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def push_statefile(self, node):
        self.set_statefile()
        self._push_statefile(node)
        self.get_peersenders()
        for s in self.peersenders:
            self._push_statefile(s)

    def apply_delta(self, node):
        if not which('dds'):
            raise ex.excError("dds executable not found")
        dst = self.dsts[node]
        extract_cmd = ['dds', '--extract', '--cow', self.snap1_cow, '--source',
                       self.snap2]
        merge_cmd = ['dds', '--merge', '--dest', dst, '-v']
        merge_cmd = rcEnv.rsh.split() + [node] + merge_cmd
        self.log.info(' '.join(extract_cmd + ["|"] + merge_cmd))
        p1 = Popen(extract_cmd, stdout=PIPE)
        pi = Popen(["dd", "bs=4096"], stdin=p1.stdout, stdout=PIPE, stderr=PIPE)
        p2 = Popen(merge_cmd, stdin=pi.stdout, stdout=PIPE)
        buff = p2.communicate()
        if p2.returncode == 0:
            stats_buff = pi.communicate()[1]
            stats = self.parse_dd(stats_buff)
            self.update_stats(stats, target=node)
        else:
            if buff[1] is not None and len(buff[1]) > 0:
                self.log.error(buff[1])
            self.log.error("sync update failed")
            raise ex.excError
        if buff[0] is not None and len(buff[0]) > 0:
            self.log.info(buff[0])

    def do_update(self, node):
        self.apply_delta(node)

    def remove_snap1(self):
        if not self.snap_exists(self.snap1):
            return
        cmd = ['lvremove', '-f', self.snap1]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def rename_snap2(self):
        if not self.snap_exists(self.snap2):
            self.log.error("%s should exist"%self.snap2)
            raise ex.excError
        if self.snap_exists(self.snap1):
            self.log.error("%s should not exist"%self.snap1)
            raise ex.excError
        cmd = ['lvrename', self.src_vg, self.snap2_lv, self.snap1_lv]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def rotate_snaps(self):
        self.remove_snap1()
        self.rename_snap2()

    def check_remote(self, node):
        rs = self.get_remote_state(node)
        if self.snap1_uuid != rs['uuid']:
            self.log.error("%s last update uuid doesn't match snap1 uuid"%(node))
            raise ex.excError

    def get_remote_state(self, node):
        self.set_statefile()
        cmd1 = ['env', 'LANG=C', 'cat', self.statefile]
        cmd = rcEnv.rsh.split() + [node] + cmd1
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            self.log.error("could not fetch %s last update uuid"%node)
            raise ex.excError
        return self.parse_statefile(out, node=node)

    def get_local_state(self):
        self.set_statefile()
        with open(self.statefile, 'r') as f:
            out = f.read()
        return self.parse_statefile(out)

    def parse_statefile(self, out, node=None):
        self.set_statefile()
        if node is None:
            node = rcEnv.nodename
        lines = out.strip().split('\n')
        if len(lines) != 1:
            self.log.error("%s:%s is corrupted"%(node, self.statefile))
            raise ex.excError
        fields = lines[0].split(';')
        if len(fields) != 2:
            self.log.error("%s:%s is corrupted"%(node, self.statefile))
            raise ex.excError
        return dict(date=fields[0], uuid=fields[1])

    def sync_nodes(self):
        if self.target != ['nodes']:
            return
        self.sync_update()

    def sync_drp(self):
        if self.target != ['drpnodes']:
            return
        self.sync_update()

    def sync_update(self):
        if not self.svc_syncable():
            return
        for n in self.targets:
            self.do_update(n)
        self.rotate_snaps()
        self.write_statefile()
        for n in self.targets:
            self.push_statefile(n)
        self.write_stats()

    def checksum(self, node, bdev, q=None):
        cmd = ['md5sum', bdev]
        if node != rcEnv.nodename:
            cmd = rcEnv.rsh.split() + [node] + cmd
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            return ""
        o = out.split()
        if q is not None:
            q.put(o[0])
        else:
            self.checksums[node] = o[0]

    def sync_verify(self):
        if not self.svc_syncable():
            return
        self.get_info()
        from multiprocessing import Process, Queue
        self.checksums = {}
        queues = {}
        ps = []
        self.log.info("start checksum threads. please be patient.")
        for n in self.targets:
            dst = self.dsts[n]
            queues[n] = Queue()
            p = Process(target=self.checksum, args=(n, dst, queues[n]))
            p.start()
            ps.append(p)
        self.checksum(rcEnv.nodename, self.snap1)
        self.log.info("md5 %s: %s"%(rcEnv.nodename, self.checksums[rcEnv.nodename]))
        for p in ps:
            p.join()
        for n in self.targets:
            self.checksums[n] = queues[n].get()
            self.log.info("md5 %s: %s"%(n, self.checksums[n]))
        if len(self.checksums) < 2:
            self.log.error("not enough checksums collected")
            raise ex.excError
        err = False
        for n in self.targets:
            if self.checksums[rcEnv.nodename] != self.checksums[n]:
                self.log.error("src/dst checksums differ for %s/%s"%(rcEnv.nodename, n))
                err = True
        if not err:
            self.log.info("src/dst checksums verified")

    def start(self):
        pass

    def stop(self):
        pass

    def can_sync(self, target=None):
        try:
            ls = self.get_local_state()
            last = datetime.datetime.strptime(ls['date'], "%Y-%m-%d %H:%M:%S.%f")
        except IOError:
            return True
        return not self.skip_sync(last)

    def sync_status(self, verbose=False):
        try:
            ls = self.get_local_state()
            now = datetime.datetime.now()
            last = datetime.datetime.strptime(ls['date'], "%Y-%m-%d %H:%M:%S.%f")
            delay = datetime.timedelta(minutes=self.sync_max_delay)
        except ex.excError:
            self.status_log("failed to get status")
            return rcStatus.WARN
        except IOError:
            self.status_log("dds state file not found")
            return rcStatus.WARN
        except:
            import sys
            import traceback
            e = sys.exc_info()
            print(e[0], e[1], traceback.print_tb(e[2]))
            return rcStatus.WARN
        if last < now - delay:
            self.status_log("Last sync on %s older than %i minutes"%(last, self.sync_max_delay))
            return rcStatus.WARN
        return rcStatus.UP

    def _info(self):
        data = [
          ["src", self.src],
          ["target", " ".join(self.target) if self.target else ""],
        ]
        data += self.stats_keys()
        return data

    def __init__(self,
                 rid=None,
                 target=None,
                 src=None,
                 dsts={},
                 snap_size=0,
                 **kwargs):
        resSync.Sync.__init__(self,
                              rid=rid,
                              type="sync.dds",
                              **kwargs)

        self.label = "dds of %s to %s"%(src, ", ".join(target))
        self.target = target
        self.src = src
        self.dsts = dsts
        self.snap_size = snap_size

    def __str__(self):
        return "%s target=%s src=%s" % (resSync.Sync.__str__(self),\
                self.target, self.src)

   0707010005c9f6000081b40000000000000000000000015ab217540000158e000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/resSyncDocker.py   import os
from subprocess import Popen, PIPE

import rcStatus
from rcUtilities import justcall
from rcGlobalEnv import rcEnv
import resSync
import rcExceptions as ex

os.environ['LANG'] = 'C'

class SyncDocker(resSync.Sync):
    def __init__(self,
                 rid=None,
                 target=None,
                 **kwargs):
        resSync.Sync.__init__(self,
                              rid=rid,
                              type="sync.docker",
                              **kwargs)

        self.label = "docker img sync to %s" % ", ".join(target)
        self.target = target
        self.images = []
        self.image_id_name = {}
        self.dstfs = None
        self.dst = "docker images"
        self.targets = set()

    def _info(self):
        data = [
          ["target", " ".join(self.target) if self.target else ""],
        ]
        data += self.stats_keys()
        return data

    def get_docker_data_dir_svc_fs(self):
        l = []
        for r in self.svc.get_resources("fs"):
            l.append(r.mount_point)
        l = sorted(l)
        v = self.svc.dockerlib.docker_data_dir.split("/")
        while len(v) > 0:
            path = "/".join(v)
            if path in l:
                return path
            v = v[:-1]

    def get_images(self):
        for r in self.svc.get_resources("container.docker"):
            image_id = self.svc.dockerlib.get_run_image_id(r)
            self.image_id_name[image_id] = r.run_image
            self.images.append(image_id)

    def get_remote_images(self, node):
        ruser = self.svc.node.get_ruser(node)
        cmd = rcEnv.rsh.split()+['-l', ruser, node, '--', rcEnv.paths.svcmgr, '-s', self.svc.svcname, "docker", "images", "-a", "--no-trunc"]
        out, err, ret = justcall(cmd)
        images = []
        for line in out.split('\n'):
            l = line.split()
            if len(l) < 3:
                continue
            if l[0] == "REPOSITORY":
                continue
            images.append(l[2])
        return images

    def on_add(self):
        self.dstfs = self.get_docker_data_dir_svc_fs()

    def get_targets(self, action=None):
        self.targets = set()
        if 'nodes' in self.target and action in (None, 'sync_nodes'):
            self.targets |= self.svc.nodes
        if 'drpnodes' in self.target and action in (None, 'sync_drp'):
            self.targets |= self.svc.drpnodes
        self.targets -= set([rcEnv.nodename])
        for node in self.targets.copy():
            if node in self.svc.nodes:
                target = 'nodes'
            elif node in self.svc.drpnodes:
                target = 'drpnodes'
            else:
                continue
            try:
                mounted = self.remote_fs_mounted(node)
            except ex.excError:
                mounted = False
            if not mounted:
                self.targets -= set([node])

    def can_sync(self, target=None):
        return True

    def sync_nodes(self):
        self._sync_update('sync_nodes')

    def sync_drp(self):
        self._sync_update('sync_drp')

    def sanity_checks(self):
        self.pre_sync_check_flex_primary()
        self.pre_sync_check_svc_not_up()

    def _sync_update(self, action):
        try:
            self.sanity_checks()
        except ex.excError:
            return
        self.get_targets(action)
        if len(self.targets) == 0:
            return
        self.get_images()
        for node in self.targets:
            remote_images = self.get_remote_images(node)
            missing = set(self.images) - set(remote_images)
            for image in missing:
                self.save_load(node, image)
        self.write_stats()

    def save_load(self, node, image):
        ruser = self.svc.node.get_ruser(node)
        save_cmd = [rcEnv.paths.svcmgr, "-s", self.svc.svcname, "docker", "save", self.image_id_name[image]]
        load_cmd = rcEnv.rsh.split(' ')+['-l', ruser, node, '--', rcEnv.paths.svcmgr, "-s", self.svc.svcname, "docker", "load"]
        self.log.info(' '.join(save_cmd) + " | " + ' '.join(load_cmd))
        p1 = Popen(save_cmd, stdout=PIPE)
        pi = Popen(["dd", "bs=4096"], stdin=p1.stdout, stdout=PIPE, stderr=PIPE)
        p2 = Popen(load_cmd, stdin=pi.stdout, stdout=PIPE)
        out, err = p2.communicate()
        if p2.returncode == 0:
            stats_buff = pi.communicate()[1]
            stats = self.parse_dd(stats_buff)
            self.update_stats(stats, target=node)
        else:
            if err is not None and len(err) > 0:
                self.log.error(err)
            raise ex.excError("sync update failed")
        if out is not None and len(out) > 0:
            self.log.info(out)

    def sync_status(self, verbose=False):
        self.get_targets()
        if len(self.targets) == 0:
            self.status_log("no target nodes")
            return rcStatus.NA
        self.get_images()
        total_missing = 0
        for node in self.targets:
            remote_images = self.get_remote_images(node)
            missing = set(self.images) - set(remote_images)
            n_missing = len(missing)
            total_missing += n_missing
            if n_missing > 0:
                if n_missing > 1:
                    plural = "s"
                else:
                    plural = ""
                self.status_log("target node %s miss image%s %s" % (node, plural, ','.join(missing)))
        if total_missing > 0:
            return rcStatus.WARN
        return rcStatus.UP


  0707010005c9f7000081b40000000000000000000000015ab21754000029d8000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/resSyncEvasnap.py  import os
import logging

from rcGlobalEnv import rcEnv
from rcUtilities import which
import rcExceptions as ex
import rcStatus
import time
import datetime
import xml.etree.ElementTree as ET
import subprocess
import resSync

class syncEvasnap(resSync.Sync):
    def wait_for_devs_ready(self):
        pass

    def can_sync(self, target=None):
        ts = None

        """ get oldest snap
        """
        for pair in self.pairs:
            info = self.lun_info(pair['dst'])
            if info is None:
                self.log.debug("snap %s missing"%pair['dst'])
                return True
            _ts = info['creationdatetime']
            if ts is None or _ts < ts:
                ts = _ts
        return not self.skip_sync(ts)

    def recreate(self):
        def snapname(info):
            return info['objectname'].split('\\')[-2]+'_'+self.snap_name

        try:
            self.prereq()
        except ex.excError as e:
            self.log.error(str(e))
            raise ex.excError

        status = self._status(skip_prereq=True)

        if not self.can_sync():
            return

        if not self.svc.options.force and status == rcStatus.UP:
            self.log.info("snapshots are already fresh. use --force to bypass")
            return

        cmd = []
        for pair in self.pairs:
            if pair['dst'] in self._lun_info:
                info = self._lun_info[pair['dst']]
                for mask in pair['mask']:
                    lunid = int(mask.split('\\')[-1])
                    hostpath = '\\'.join(mask.split('\\')[:-1])
                    if hostpath in info['mask'] and info['mask'][hostpath] == lunid:
                        cmd += ['delete lun "%s"'%mask]
                cmd += ['delete vdisk "%s" wait_for_completion'%info['objectname']]
        self.sssu(cmd, verbose=True)

        cmd = []
        for pair in self.pairs:
            info = self.lun_info(pair['src'])
            if 'allocation_policy' in pair:
                policy = str(pair['allocation_policy']).lower()
            else:
                policy = 'demand'
            if policy not in ['demand', 'fully']:
                policy = 'demand'
            if 'vraid' in pair and pair['vraid'] in ['vraid6', 'vraid5', 'vraid0', 'vraid1']:
                force_vraid = "redundancy=%s"%pair['vraid']
            else:
                force_vraid = ""
            cmd += ['add snapshot %s vdisk="%s" allocation_policy=%s world_wide_lun_name=%s %s'%(snapname(info), info['objectname'], policy, self.convert_wwid(pair['dst']), force_vraid)]
        self.sssu(cmd, verbose=True)

        cmd = []
        for pair in self.pairs:
            info = self.lun_info(pair['dst'])
            for mask in pair['mask']:
                lunid = mask.split('\\')[-1]
                hostpath = '\\'.join(mask.split('\\')[:-1])
                cmd += ['add lun %s host="%s" vdisk="%s"'%(lunid, hostpath, snapname(info))]
        self.sssu(cmd, verbose=True)

    def sssu(self, cmd=[], verbose=False, check=True):
        os.chdir(rcEnv.paths.pathtmp)
        cmd = [self.sssubin,
               "select manager %s username=%s password=%s"%(self.manager, self.username, self.password),
               "select system %s"%self.eva_name] + cmd
        if verbose:
            import re
            from copy import copy
            _cmd = copy(cmd)
            _cmd[1] = re.sub(r'password=.*', 'password=xxxxx', _cmd[1])
            self.log.info(subprocess.list2cmdline(_cmd))
            ret, out, err = self.call(cmd)
            if 'Error:' in out > 0:
                self.log.error(out)
            else:
                self.log.info(out)
        else:
            ret, out, err = self.call(cmd)
        if check and "Error" in out:
            raise ex.excError("sssu command execution error")
        return ret, out, err

    def lun_info(self, wwid):
        if wwid in self._lun_info:
            return self._lun_info[wwid]

        if '-' not in wwid:
            _wwid = wwid
            wwid = self.convert_wwid(wwid)

        info = {
            'oxuid': 'unknown',
            'lunid': -1,
            'creationdatetime': datetime.datetime(year=datetime.MINYEAR, month=1, day=1),
            'mask': {}
        }
        try:
            ret, out, err = self.sssu(["find vdisk lunwwid="+wwid+" xml"])
        except:
            return None
        l = out.split('\n')
        for i, line in enumerate(l):
            if line == '<object>':
                l = l[i:]
                break
        e = ET.fromstring('\n'.join(l))

        for p in e.findall("presentations/presentation"):
            host = p.find("hostname").text
            lunid = p.find("lunnumber").text
            info['mask'][host] = int(lunid)

        e_oxuid = e.find("objectparenthexuid")
        if e_oxuid is not None:
            info['oxuid'] = e_oxuid.text.replace('-','')

        e_objectname = e.find("objectname")
        if e_objectname is not None:
            info['objectname'] = e_objectname.text

        e_creationdatetime = e.find("creationdatetime")
        if e_oxuid is not None:
            try:
                creationdatetime = datetime.datetime.strptime(e_creationdatetime.text, "%d-%b-%Y %H:%M:%S")
                info['creationdatetime'] = creationdatetime
            except:
                self.log.error("failed to parse snapshot creation datetime")
                pass

        self._lun_info[_wwid] = info

        return info

    def _status(self, verbose=False, skip_prereq=False):
        err = False
        errlog = []
        try:
            if not skip_prereq:
                self.prereq()
        except ex.excError as e:
            self.status_log(str(e))
            return rcStatus.WARN
        for pair in self.pairs:
            info_s = self.lun_info(pair['src'])
            info_d = self.lun_info(pair['dst'])
            if info_s is None:
                errlog.append("snapshot source %s does not exists"%pair['src'])
                err |= True
                continue
            if info_d is None:
                errlog.append("snapshot %s does not exists"%pair['dst'])
                err |= True
                continue
            if info_s['oxuid'].lower() != info_d['oxuid'].lower():
                errlog.append("snapshot %s exists but incorrect parent object uid: %s"%(pair['dst'], info_d['oxuid'].lower()))
                err |= True
            if info_d['creationdatetime'] < datetime.datetime.now() - datetime.timedelta(minutes=self.sync_max_delay):
                errlog.append("snapshot %s too old"%pair['dst'])
                err |= True
            for mask in pair['mask']:
                hostpath = '\\'.join(mask.split('\\')[:-1])
                hostname = mask.split('\\')[-2]
                dstlunid = mask.split('\\')[-1]
                if hostpath not in info_d['mask']:
                    errlog.append("snapshot %s exists but not presented to host %s"%(pair['dst'], hostname))
                    err |= True
                    continue
                try:
                    dstlunid = int(dstlunid)
                    if info_d['mask'][hostpath] != dstlunid:
                        errlog.append("snapshot %s exists but incorrect lunid for host %s"%(pair['dst'], hostname))
                        err |= True
                except ValueError:
                    pass
        if err:
            self.status_log('. '.join(errlog))
            return rcStatus.WARN
        return rcStatus.UP

    def sync_resync(self):
        self.recreate()

    def sync_update(self):
        self.recreate()

    def refresh_svcstatus(self):
        self.svcstatus = self.svc.group_status(excluded_groups=set(["sync", 'hb']))

    def get_svcstatus(self):
        if len(self.svcstatus) == 0:
            self.refresh_svcstatus()

    def convert_wwid(self, wwid):
        s = ""
        for i, c in enumerate(wwid):
            s += c
            if (i+1) % 4 == 0: s += '-'
        return s.strip('-')

    def prereq(self):
        import ConfigParser
        if not os.path.exists(self.conf):
            raise ex.excError("missing %s"%self.conf)
        self.config = ConfigParser.RawConfigParser()
        self.config.read(self.conf)
        if not self.config.has_section(self.eva_name):
            raise ex.excError("no credentials for array %s in %s"%(self.eva_name, self.conf))
        if not self.config.has_option(self.eva_name, "manager"):
            raise ex.excError("no manager set for array %s in %s"%(self.eva_name, self.conf))
        if not self.config.has_option(self.eva_name, "username"):
            raise ex.excError("no username set for array %s in %s"%(self.eva_name, self.conf))
        if not self.config.has_option(self.eva_name, "password"):
            raise ex.excError("no password set for array %s in %s"%(self.eva_name, self.conf))
        self.manager = self.config.get(self.eva_name, "manager")
        self.username = self.config.get(self.eva_name, "username")
        self.password = self.config.get(self.eva_name, "password")
        try:
            self.sssubin = self.config.get(self.eva_name, "bin")
        except:
            self.sssubin = None

        if self.sssubin:
            sssubin = which(self.sssubin)
        else:
            sssubin = None

        if not sssubin:
            raise ex.excError("missing %s"%self.sssubin)

        if not self.sssubin:
            # sssu in PATH and not specified in auth.conf
            self.sssubin = sssubin

        for pair in self.pairs:
            if 'src' not in pair or 'dst' not in pair or 'mask' not in pair:
                raise ex.excError("missing parameter in pair %s"%str(pair))
        ret, out, err = self.sssu(check=False)
        if "Error opening https connection" in out:
            raise ex.excError("error login to %s"%self.manager)
        elif "Error" in out:
            raise ex.excError("eva %s is not managed by %s"%(self.eva_name, self.manager))

    def __init__(self,
                 rid=None,
                 pairs=[],
                 eva_name="",
                 snap_name="",
                 **kwargs):
        resSync.Sync.__init__(self,
                              rid=rid, type="sync.evasnap",
                              **kwargs)

        self.label = "EVA snapshot %s"%(rid)
        self.eva_name = eva_name
        self.snap_name = snap_name
        self.pairs = pairs
        self.conf = os.path.join(rcEnv.paths.pathetc, 'auth.conf')
        self._lun_info = {}
        self.default_schedule = "@0"

    def __str__(self):
        return "%s eva_name=%s pairs=%s" % (resSync.Sync.__str__(self),\
                self.eva_name, str(self.pairs))

0707010005c9f8000081b40000000000000000000000015ab217540000272a000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/resSyncHp3par.py   import os

from rcGlobalEnv import rcEnv
import rcExceptions as ex
import rcStatus
import time
import resSync
import datetime
import rcHp3par as rc

class syncHp3par(resSync.Sync):
    def __init__(self,
                 rid=None,
                 array=None,
                 method=None,
                 mode=None,
                 rcg_names={},
                 **kwargs):
        resSync.Sync.__init__(self,
                              rid=rid,
                              type="sync.hp3par",
                              **kwargs)
        self.array = array
        self.rcg_names = rcg_names
        self.rcg = rcg_names[array]
        self.mode = mode
        self.method = method
        self.label = "hp3par %s %s"%(mode, self.rcg)
        self.array_obj = None
        self.remote_array_obj = None

    def __str__(self):
        return "%s array=%s method=%s mode=%s rcg=%s" % (
                resSync.Sync.__str__(self),
                self.array,
                self.method,
                self.mode,
                self.rcg)

    def on_add(self):
        try:
            arrays = rc.Hp3pars(objects=[self.array], log=self.log)
        except Exception as e:
            raise ex.excError(str(e))
        if len(arrays.arrays) == 1:
            self.array_obj = arrays.arrays[0]
        if self.array_obj is None:
            raise ex.excError("array %s is not accessible" % self.array)
        self.array_obj.svcname = self.svc.svcname

    def get_array_obj(self, target=None, log=False):
        if target is None:
            array_name = self.array
            return self.array_obj
        else:
            array_name = target
            if self.remote_array_obj is None:
                try:
                    self.remote_array_obj = rc.Hp3pars(objects=[target], log=self.log).arrays[0]
                    if self.remote_array_obj is None:
                        raise ex.excError("array %s is not accessible" % array_name)
                    self.remote_array_obj.svcname = self.svc.svcname
                    return self.remote_array_obj
                except Exception as e:
                    raise ex.excError(str(e))

    def _cmd(self, cmd, target=None, log=False):
        array_obj = self.get_array_obj(target=target, log=self.log)
        if log:
            if target is not None:
                suffix = " (on " + target + ")"
            else:
                suffix = ""
            self.log.info(cmd+suffix)

        if log:
            out, err = array_obj.rcmd(cmd, log=log)
        else:
            out, err = array_obj.rcmd(cmd)

        if not log:
            return out, err
        if len(out) > 0:
            self.log.info(out)
        if len(err) > 0:
            self.log.error(err)
            raise ex.excError()
        return out, err

    def can_sync(self, target=None, s=None):
        data = self.showrcopy()
        try:
            last = data['vv'][0]['LastSyncTime']
        except IndexError:
            return False
        if self.skip_sync(datetime.datetime.utcnow()-last):
            return False
        return True

    def sync_resync(self):
        self.sync_update()

    def sync_swap(self):
        data = self.showrcopy()
        if data['rcg']['Role'] == 'Primary':
            self.log.error("rcopy group %s role is Primary. refuse to swap")
            raise ex.excError()
        self.stoprcopygroup()
        self.setrcopygroup_reverse()
        self.startrcopygroup()

    def sync_update(self):
        self.syncrcopygroup()

    def sync_revert(self):
        self.setrcopygroup_revert()

    def sync_resume(self):
        self.startrcopygroup()

    def sync_quiesce(self):
        self.stoprcopygroup()

    def sync_break(self):
        self.stoprcopygroup()

    def start(self):
        data = self.showrcopy()
        target = data['rcg']['Target']
        if self.is_splitted(target):
            self.log.info("we are split from %s array" % target)
            self.start_splitted()
        else:
            self.log.info("we are joined with %s array" % target)
            self.start_joined()

    def start_joined(self):
        data = self.showrcopy()
        if data['rcg']['Role'] == 'Primary':
            self.log.info("rcopy group %s role is already Primary. skip" % self.rcg)
            return
        self.stoprcopygroup()
        self.setrcopygroup_reverse()

    def start_splitted(self):
        self.setrcopygroup_failover()

    def stop(self):
        pass

    def setrcopygroup_revert(self):
        data = self.showrcopy()
        if data['rcg']['Role'] != 'Primary-Rev':
           self.log.error("rcopy group %s role is not Primary-Rev. refuse to setrcopygroup revert" % self.rcg)
           return
        self._cmd("setrcopygroup reverse -f -waittask -stopgroups -local -current %s" % self.rcg, log=True)
        self.clear_caches()

    def setrcopygroup_failover(self):
        data = self.showrcopy()
        if data['rcg']['Role'] == 'Primary-Rev':
           self.log.info("rcopy group %s role is already Primary-Rev. skip setrcopygroup failover" % self.rcg)
           return
        self._cmd("setrcopygroup failover -f -waittask %s" % self.rcg, log=True)
        self.clear_caches()

    def setrcopygroup_reverse(self):
        data = self.showrcopy()
        if data['rcg']['Role'] == 'Primary':
            self.log.info("rcopy group %s role is already Primary. skip setrcopygroup reverse" % self.rcg)
            return
        self._cmd("setrcopygroup reverse -f -waittask %s" % self.rcg, log=True)
        self.clear_caches()

    def syncrcopygroup(self):
        data = self.showrcopy()
        if data['rcg']['Role'] != 'Primary':
            self.log.info("rcopy group %s role is not Primary. skip sync" % self.rcg)
            return
        if data['rcg']['Mode'] == 'Periodic':
            self.log.info("skip syncrcopy as group %s is in periodic mode" % self.rcg)
            return
        self._cmd("syncrcopy -w %s" % self.rcg, log=True)
        self.clear_caches()

    def startrcopygroup(self):
        data = self.showrcopy()
        if data['rcg']['Status'] == "Started":
            self.log.info("rcopy group %s is already started. skip startrcopygroup" % self.rcg)
            return
        if data['rcg']['Role'] != 'Primary':
            self.log.error("rcopy group %s role is not Primary. refuse to start rcopy" % self.rcg)
            raise ex.excError()
        self._cmd("startrcopygroup %s" % self.rcg, log=True)
        self.clear_caches()

    def stoprcopygroup(self):
        data = self.showrcopy()
        if data['rcg']['Status'] == "Stopped":
            self.log.info("rcopy group %s is already stopped. skip stoprcopygroup" % self.rcg)
            return
        if data['rcg']['Role'] == "Primary":
            self._cmd("stoprcopygroup -f %s" % self.rcg, log=True)
        else:
            target = data['rcg']['Target']
            self._cmd("stoprcopygroup -f %s" % self.rcg_names[target], target=target, log=True)
        self.clear_caches()

    def is_splitted(self, target):
        data = self.showrcopy_links()
        for h in data:
            if h['Target'] != target:
                continue
            if h['Status'] == "Up":
                return False
        return True

    def showrcopy_links(self):
        """
        Target,Node,Address,Status,Options
        baie-pra,0:2:4,20240002AC00992B,Down,
        baie-pra,1:2:3,21230002AC00992B,Down,
        receive,0:2:4,20240002AC00992B,Up,
        receive,1:2:3,21230002AC00992B,Up,
        """
        out, err = self._cmd("showrcopy links")
        cols = ["Target", "Node", "Address", "Status", "Options"]
        lines = out.split('\n')
        data = []
        for line in lines:
            v = line.strip().split(",")
            if len(v) != len(cols):
                continue
            h = {}
            for a, b in zip(cols, v):
                h[a] = b
            data.append(h)
        return data

    def clear_caches(self):
        self.array_obj.clear_showrcopy_cache()

    def showrcopy(self):
        return self.array_obj.showrcopy(self.rcg)

    def sync_status(self, verbose=False):
        if self.array_obj is None:
            self.status_log("array %s is not accessible" % self.array)
            return rcStatus.WARN

        try:
            data = self.showrcopy()
        except ex.excError as e:
            self.status_log(str(e))
            return rcStatus.WARN

        elapsed = datetime.datetime.utcnow() - datetime.timedelta(minutes=self.sync_max_delay)
        r = None
        if data['rcg']['Status'] != "Started":
            self.status_log("rcopy group status is not Started (%s)"%data['rcg']['Status'])
            r = rcStatus.WARN
        if self.mode == "async" and data['rcg']['Mode'] != "Periodic":
            self.status_log("rcopy group mode is not Periodic (%s)"%data['rcg']['Mode'])
            r = rcStatus.WARN
        if self.mode == "sync" and data['rcg']['Mode'] != "Sync":
            self.status_log("rcopy group mode is not Sync (%s)"%data['rcg']['Mode'])
            r = rcStatus.WARN
        if self.mode == "async":
            l = [o for o in data['rcg']['Options'] if o.startswith('Period ')]
            if len(l) == 0:
                self.status_log("rcopy group period option is not set")
                r = rcStatus.WARN
        if 'auto_recover' not in data['rcg']['Options']:
            self.status_log("rcopy group auto_recover option is not set")
            r = rcStatus.WARN
        for vv in data['vv']:
            if vv['SyncStatus'] != 'Synced':
                self.status_log("vv %s SyncStatus is not Synced (%s)"%(vv['LocalVV'], vv['SyncStatus']))
                r = rcStatus.WARN
            if vv['LastSyncTime'] < elapsed:
                self.status_log("vv %s last sync too old (%s)"%(vv['LocalVV'], vv['LastSyncTime'].strftime("%Y-%m-%d %H:%M")))
                r = rcStatus.WARN

        if r is not None:
            return r

        return rcStatus.UP

  0707010005c9f9000081b40000000000000000000000015ab2175400000d2d000000660000000000000000000000000000003000000000root/usr/share/opensvc/lib/resSyncHp3parSnap.py   import os

from rcGlobalEnv import rcEnv
import rcExceptions as ex
import rcStatus
from rcUtilities import justcall
import resSync
import datetime
import rcHp3par as rc

class syncHp3parSnap(resSync.Sync):
    def __init__(self,
                 rid=None,
                 array=None,
                 vv_names=[],
                 **kwargs):
        resSync.Sync.__init__(self,
                              rid=rid,
                              type="sync.hp3parsnap",
                              **kwargs)
        self.array = array
        self.vv_names = vv_names
        self.label = "hp3parsnap %s" % ", ".join(self.vv_names)
        if len(self.label) > 50:
            self.label = self.label[:47] + "..."
        self.default_schedule = "@0"

    def __str__(self):
        return self.label

    def on_add(self):
        try:
            arrays = rc.Hp3pars(objects=[self.array], log=self.log)
        except Exception as e:
            raise ex.excInitError(str(e))
        if len(arrays.arrays) == 1:
            self.array_obj = arrays.arrays[0]
        else:
            self.array_obj = None

        if self.array_obj is None:
            self.log.error("no 3par array object")
            return
        self.array_obj.svcname = self.svc.svcname

    def can_sync(self, target=None, s=None):
        data = self.showvv()
        if len(data) < len(self.vv_names):
            return False
        last = self.lastsync_s_to_datetime(data[0]['CreationTime'])
        if self.skip_sync(datetime.datetime.utcnow()-last):
            return False
        try:
            self.check_requires("sync_update")
        except ex.excError:
            return False
        return True

    def updatevv(self):
        self.array_obj.updatevv(vvnames=self.vv_names, log=self.log)

    def sync_update(self):
        self.updatevv()
        self.array_obj.clear_caches()

    def lastsync_s_to_datetime(self, s):
        out, err, ret = justcall(["date", "--utc", "--date=%s" % s, '+%Y-%m-%d %H:%M:%S'])
        d = datetime.datetime.strptime(out.strip(), "%Y-%m-%d %H:%M:%S")
        return d

    def showvv(self):
        return self.array_obj.showvv(vvprov="snp", vvnames=self.vv_names, cols=["Name", "CreationTime"])

    def _status(self, verbose=False):
        if self.array_obj is None:
            self.status_log("array %s is not accessible" % self.array)
            return rcStatus.WARN
        if not self.array_obj.has_virtualcopy():
            self.status_log("array %s has no virtual copy license" % self.array)
            return rcStatus.WARN

        try:
            data = self.showvv()
        except ex.excError as e:
            self.status_log(str(e))
            return rcStatus.WARN

        r = None
        if len(data) < len(self.vv_names):
            missing = set(self.vv_names) - set([d["Name"] for d in data])
            for m in missing:
                self.status_log("missing vv: %s" % m)
            r = rcStatus.WARN

        elapsed = datetime.datetime.utcnow() - datetime.timedelta(minutes=self.sync_max_delay)
        for vv in data:
            if self.lastsync_s_to_datetime(vv['CreationTime']) < elapsed:
                self.status_log("vv %s last sync too old (%s)"%(vv['Name'], vv['CreationTime']))
                r = rcStatus.WARN

        if r is not None:
            return r

        return rcStatus.UP

   0707010005c9fa000081b40000000000000000000000015ab21754000021bf000000660000000000000000000000000000002f00000000root/usr/share/opensvc/lib/resSyncIbmdsSnap.py    import os
import logging

from rcGlobalEnv import rcEnv
from rcUtilities import which
import rcExceptions as ex
import rcStatus
import time
import datetime
import resSync
import rcIbmDs

class syncIbmdsSnap(resSync.Sync):
    def resyncflash(self):
        if self.array is None:
            self.array = rcIbmDs.IbmDss().get(self.arrayname)
        data = self.lsflash()
        ese_pairs = []
        other_pairs = []

        for d in data:
            if d['isTgtSE'] == 'ESE':
                ese_pairs.append(d['ID'])
            else:
                other_pairs.append(d['ID'])

        present_pairs = set(map(lambda x: x['ID'], data))
        missing_pairs = list(set(self.pairs) - present_pairs)
        if len(missing_pairs) > 0:
            missing_pairs.sort()
            raise ex.excError("refuse to resync as %s pairs are not currently configured"%', '.join(missing_pairs))

        self._resyncflash(ese_pairs, '-tgtse')
        self._resyncflash(other_pairs)

    def _resyncflash(self, pairs, options=None):
        if len(pairs) == 0:
            return
        if self.recording:
            self._resyncflash_recording(pairs, options=options)
        else:
            self._resyncflash_norecording(pairs, options=options)

    def _resyncflash_norecording(self, pairs, options=None):
        s = 'rmflash -dev %s -quiet' % self.arrayname
        l = [s]
        l.append(' '.join(pairs))
        cmd = ' '.join(l)
        out, err = self.array.dscli(cmd, log=self.log)
        if len(err) > 0:
            raise ex.excError(err)
        s = 'mkflash -dev %s -persist' % self.arrayname
        if self.bgcopy:
            s += ' -cp'
        else:
            s += ' -nocp'
        l = [s]
        if options is not None:
            l.append(options)
        l.append(' '.join(pairs))
        cmd = ' '.join(l)
        out, err = self.array.dscli(cmd, log=self.log)
        if len(err) > 0:
            raise ex.excError(err)

    def _resyncflash_recording(self, pairs, options=None):
        s = 'resyncflash -dev %s -persist -record' % self.arrayname
        if self.bgcopy:
            s += ' -cp'
        else:
            s += ' -nocp'
        l = [s]
        if options is not None:
            l.append(options)
        l.append(' '.join(pairs))
        cmd = ' '.join(l)
        out, err = self.array.dscli(cmd, log=self.log)
        if len(err) > 0:
            raise ex.excError(err)

    def can_sync(self, target=None):
        self.get_last()
        if self.skip_sync(self.last):
            return False
        return True

    def get_last(self, data=None):
        if data is None:
            data = self.lsflash()
        if len(data) == 0:
            return
        lastsync = datetime.datetime.now()
        for _data in data:
            _lastsync = _data['DateSynced']
            try:
                _lastsync = datetime.datetime.strptime(_lastsync, "%a %b %d %H:%M:%S %Z %Y")
            except ValueError:
                # workaround hp-ux python 2.6
                _lastsync = _lastsync.replace("CET", "MET")
                _lastsync = datetime.datetime.strptime(_lastsync, "%a %b %d %H:%M:%S %Z %Y")

            if _lastsync < lastsync:
                lastsync = _lastsync
        self.last = lastsync

    def _status(self, verbose=False):
        try:
            data = self.lsflash()
            self.get_last(data)
        except ex.excError as e:
            self.status_log(str(e))
            return rcStatus.WARN
        r = rcStatus.UP

        record_disabled = []
        persist_disabled = []
        record_enabled = []
        state_invalid = []

        for _data in data:
            if _data['Recording'] == "Disabled":
                record_disabled.append(_data['ID'])
            elif _data['Recording'] == "Enabled":
                record_enabled.append(_data['ID'])
            if _data['State'] != "Valid":
                state_invalid.append(_data['ID'])
            if _data['Persistent'] == "Disabled":
                persist_disabled.append(_data['ID'])

        if self.recording and len(record_disabled) > 0:
            self.status_log("Recording disabled on %s"%','.join(record_disabled))
            r = rcStatus.WARN
        elif not self.recording and len(record_enabled) > 0:
            self.status_log("Recording enabled on %s"%','.join(record_enabled))
            r = rcStatus.WARN
        if len(state_invalid) > 0:
            self.status_log("State not valid on %s"%','.join(state_invalid))
            r = rcStatus.WARN
        if len(persist_disabled) > 0:
            self.status_log("Persistent disabled on %s"%','.join(persist_disabled))
            r = rcStatus.WARN

        pairs = []
        for d in data:
            if 'ID' not in d:
                continue
            pairs.append(d['ID'])
        missing = set(self.pairs) - set(pairs)
        missing = sorted(list(missing))
        if len(missing) > 0:
            self.status_log("Missing flashcopy on %s"%','.join(missing))
            r = rcStatus.WARN

        if self.last is None:
            return rcStatus.WARN
        elif self.last < datetime.datetime.now() - datetime.timedelta(minutes=self.sync_max_delay):
            self.status_log("Last sync on %s older than %d minutes"%(self.last, self.sync_max_delay))
            return rcStatus.WARN
        elif r == rcStatus.WARN:
            return rcStatus.WARN
        self.status_log("Last sync on %s"%self.last)
        return rcStatus.UP

    def sync_break(self):
        pass

    def sync_resync(self):
        self.resyncflash()

    def sync_update(self):
        self.resyncflash()

    def start(self):
        pass

    def __init__(self,
                 rid=None,
                 pairs=[],
                 array=None,
                 bgcopy=True,
                 recording=True,
                 **kwargs):
        resSync.Sync.__init__(self,
                              rid=rid,
                              type="sync.ibmdssnap",
                              **kwargs)

        self.label = "flash copy %s"%','.join(pairs)
        self.pairs = pairs
        self.arrayname = array
        self.recording = recording
        self.bgcopy = bgcopy
        self.array = None
        self.last = None
        self.params = "setenv -banner off -header on -format delim\n"
        self.default_schedule = "@0"

    def lsflash(self):
        if self.array is None:
            self.array = rcIbmDs.IbmDss().get(self.arrayname)
        out, err = self.array.dscli(self.params+'lsflash -l -dev %s ' % self.arrayname + ' '.join(self.pairs))
        if 'No Flash Copy found' in out:
            return []
        data = self.parseblock(0, out)
        return data

    def getblock(self, n, s):
        lines = s.replace('dscli> ', '').split('\n')
        begin = None
        end = None
        met = 0
        for i, line in enumerate(lines):
             if line.startswith("==="):
                 met += 1
                 if met < n:
                     continue
                 if begin is None:
                     begin = i-1
                 else:
                     end = i-1
                     break
        if end is None:
           end = i
        return lines[begin:end]

    def parseblock(self, n, out):
        data = []
        lines = self.getblock(n, out)
        if len(lines) < 3:
            return
        headers = lines[0].split(',')
        headers_multipliers = []
        for i, h in enumerate(headers):
            if '^' not in h:
                headers_multipliers.append(None)
                continue
            x = h[h.index('^')+1:h.index('B)')]
            x = int(x)
            headers_multipliers.append((2**x)/1024/1024)
            stripped_header = headers[i][:headers[i].index(' (')]
            while stripped_header in headers:
                stripped_header += "_"
            headers[i] = stripped_header
        for line in lines[2:]:
            d = {}
            l = line.split(',')
            for i, key in enumerate(headers):
                if i >= len(l):
                    raise ex.excError("the command dataset does not match its advertized columning")
                key = key.strip()
                if headers_multipliers[i] is not None:
                    try:
                        d[key] = int(float(l[i]) * headers_multipliers[i])
                    except:
                        d[key] = l[i]
                else:
                    d[key] = l[i]
            data.append(d)
        return data

    def __str__(self):
        return "%s pairs=%s" % (resSync.Sync.__str__(self), ','.join(self.pairs))

 0707010005c9fb000081b40000000000000000000000015ab217540000165a000000660000000000000000000000000000003000000000root/usr/share/opensvc/lib/resSyncNecIsmSnap.py   import os
import logging

from rcGlobalEnv import rcEnv
import rcExceptions as ex
import rcStatus
import time
import datetime
import resSync
import rcNecIsm

class syncNecIsmSnap(resSync.Sync):
    def __init__(self,
                 rid=None,
                 array_name=None,
                 devs=[],
                 **kwargs):
        resSync.Sync.__init__(self,
                              rid=rid, type="sync.necismsnap",
                              **kwargs)

        self.label = "NecIsm snapshot %s"%(rid)
        self.devs = devs
        self.array = rcNecIsm.NecIsm(array_name)
        self.parse_devs(devs)
        self.default_schedule = "@0"

    def on_add(self):
        self.array.log = self.log

    def parse_devs(self, devs):
        self.svld = []
        self.sv = {}
        self.ld = {}
        devs = devs.replace(' ', ',')
        for e in devs.split(','):
            try:
                sv, ld = e.split(':')
            except:
                raise ex.excInitError("%s: malformed devs" % self.rid)
            if sv in self.sv:
                raise ex.excInitError("%s: duplicate sv %s in devs" % (self.rid, sv))
            if ld in self.ld:
                raise ex.excInitError("%s: duplicate ld %s in devs" % (self.rid, ld))
            self.sv[sv] = sv
            self.ld[ld] = ld
            self.svld.append((sv, ld))

    def wait_for_devs_ready(self):
        pass

    def get_sv_ts(self, sv):
        try:
            data = self.array.sc_query_ld(sv)
        except ex.excError:
            self.status_log("unable to query SV:%s" % sv)
            return
        if len(data["sv"]) == 0:
            self.status_log("SV:%s not found" % sv)
            return
        svinfo = data['sv'][0]
        try:
            begin = svinfo.index('[')+1
        except:
            self.status_log("unable to get timestamp for SV:%s" % sv)
            return
        end = svinfo.index(']')
        ts_s = svinfo[begin:end]
        ts = datetime.datetime.strptime(ts_s, "%Y/%m/%d %H:%M:%S")
        return ts

    def can_sync(self, target=None):
        ts = self.get_oldest_ts()
        return not self.skip_sync(ts)

    def get_oldest_ts(self):
        ts = None
        for sv, ld in self.svld:
            _ts = self.get_sv_ts(sv)
            if _ts is None:
                return
            if ts is None or _ts < ts:
                ts = _ts
        return ts

    def _status(self, verbose=False, skip_prereq=False):
        ret = 0
        ret += self._status_ts()
        ret += self._status_link()
        if ret > 0:
            return rcStatus.WARN
        return rcStatus.UP

    def _status_ts(self):
        ts = self.get_oldest_ts()
        if ts is None:
            return 1
        now = datetime.datetime.now()
        limit = now - datetime.timedelta(minutes=self.sync_max_delay)
        if ts < limit:
            self.status_log("snap too old (%s)" % ts.strftime("%Y-%m-%d %H:%M"))
            return rcStatus.WARN
        self.status_log("snap at %s" % ts.strftime("%Y-%m-%d %H:%M"))
        return 0

    def _status_link(self):
        r = 0
        for sv, ld in self.svld:
            r += self.__status_link(sv, ld)
        return r

    def __status_link(self, sv, ld):
        try:
            li = self.array.sc_linkinfo_ld(sv)
        except ex.excError:
            self.status_log("unable to get SV:%s linkinfo" % sv)
            return 1
        l = [ dst for dst in li['dst'] if ld in dst.split() and 'link' in dst.split()]
        if len(l) != 1:
            self.status_log("LD:%s is not linked to SV:%s" % (ld, sv))
            return 1
        return 0

    def sync_resync(self):
        self.unlink()
        self.create()
        self.link()

    def sync_update(self):
        self.unlink()
        self.create()
        self.link()

    def unlink(self):
        for sv, ld in self.svld:
            if self.__status_link(sv, ld) == 1:
                self.log.info("SV:%s is already unlinked from LD:%s" % (sv, ld))
            else:
                self.array.sc_unlink_ld(ld)

    def get_changing_snap(self, src):
        bv_detail = self.array.sc_query_bv_detail(src)
        l = []
        for sv, data in bv_detail['sv'].items():
            if data['Snap State'].endswith('ing'):
                l.append(':'.join((sv, data['Snap State'])))
        return l

    def wait_for_changing_snap(self, src):
        retry = 40
        while retry > 0:
            changing_snap = self.get_changing_snap(src)
            if len(changing_snap) == 0:
                return
            self.log.info("SV are changing states : %s. Wait 30sec before retry" % ', '.join(changing_snap))
            retry -= 1
            try:
                time.sleep(30)
            except:
                return

    def create(self):
        for sv, ld in self.svld:
            try:
                src = self.array.sc_query_ld(sv)['LD Name']
            except:
                raise ex.excError("can not determine source LD for SV:%s" % sv)
            self.wait_for_changing_snap(src)
            self.array.sc_create_ld(src, sv)

    def link(self):
        for sv, ld in self.svld:
            if self.__status_link(sv, ld) == 0:
                self.log.info("SV:%s is already linked to LD:%s" % (sv, ld))
            else:
                self.array.sc_link_ld(sv, ld)

    def refresh_svcstatus(self):
        self.svcstatus = self.svc.group_status(excluded_groups=set(["sync", 'hb']))

    def get_svcstatus(self):
        if len(self.svcstatus) == 0:
            self.refresh_svcstatus()

    def __str__(self):
        return "%s array_name=%s devs=%s" % (resSync.Sync.__str__(self),\
                self.array_name, str(self.devs))

  0707010005c9fc000081b40000000000000000000000015ab21754000027a0000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/resSyncNetapp.py   import os
import logging

from rcGlobalEnv import rcEnv
from rcUtilities import which, justcall
import rcExceptions as ex
import rcStatus
import time
import resSync
import datetime

class syncNetapp(resSync.Sync):
    def master(self):
        s = self.local_snapmirror_status()
        return s['master']

    def slave(self):
        s = self.local_snapmirror_status()
        return s['slave']

    def local(self):
        if rcEnv.nodename in self.filers:
            return self.filers[rcEnv.nodename]
        return None

    def _cmd(self, cmd, target, info=False):
        if target == "local":
            filer = self.local()
        elif target == "master":
            filer = self.master()
        elif target == "slave":
            filer = self.slave()
        elif target in self.filers.values():
            filer = target
        else:
            raise ex.excError("unable to find the %s filer"%target)

        _cmd = rcEnv.rsh.split() + [self.user+'@'+filer] + cmd

        if info:
            self.log.info(' '.join(_cmd))

        out, err, ret = justcall(rcEnv.rsh.split() + [self.user+'@'+filer] + cmd)

        if info:
            if len(out) > 0:
                self.log.info(out)
            if len(err) > 0:
                self.log.error(err)

        return ret, out, err

    def cmd_master(self, cmd, info=False):
        return self._cmd(cmd, "master", info=info)

    def cmd_slave(self, cmd, info=False):
        return self._cmd(cmd, "slave", info=info)

    def cmd_local(self, cmd, info=False):
        return self._cmd(cmd, "local", info=info)

    def lag_to_ts(self, lag):
        now = datetime.datetime.now()
        l = lag.split(":")
        if len(l) != 3:
            raise ex.excError("unexpected lag format")
        delta = datetime.timedelta(hours=int(l[0]),
                                   minutes=int(l[1]),
                                   seconds=int(l[2]))
        return now - delta

    def can_sync(self, target=None, s=None):
        if s is None:
            s = self.snapmirror_status(self.slave())
        ts = self.lag_to_ts(s['lag'])
        if self.skip_sync(ts):
            return False
        return True

    def lagged(self, lag, max=None):
        if max is None:
            max = self.sync_max_delay
        l = lag.split(":")
        if len(l) != 3:
            raise ex.excError("unexpected lag format")
        if int(l[0]) * 60 + int(l[1]) > max:
            return True
        return False

    def sync_resync(self):
        (ret, buff, err) = self.cmd_slave(['snapmirror', 'resync', '-f', self.slave()+':'+self.path_short], info=True)
        if ret != 0:
            raise ex.excError

    def sync_swap(self):
        master = self.master()
        slave = self.slave()
        s = self.snapmirror_status(self.local())
        if s['state'] != "Broken-off":
            self.log.error("can not swap: snapmirror is not in state Broken-off")
            raise ex.excError
        src = slave+':'+self.path_short
        dst = master+':'+self.path_short

        (ret, buff, err) = self._cmd(['snapmirror', 'resync', '-f', '-S', src, dst], master, info=True)
        if ret != 0:
            raise ex.excError(err)
        (ret, buff, err) = self._cmd(['snapmirror', 'release', self.path_short, src], master, info=True)
        if ret != 0:
            raise ex.excError(err)
        (ret, buff, err) = self._cmd(['snapmirror', 'status', '-l', dst], slave, info=False)
        if ret != 0:
            raise ex.excError(err)
        snap = ""
        state = ""
        for line in buff.split('\n'):
            l = line.split()
            if len(l) < 2:
                continue
            if l[0] == "State:":
                state = l[1]
            if state != "Broken-off":
                continue
            if l[0] == "Base" and l[1] == "Snapshot:":
                snap = l[-1]
                break
        if len(snap) == 0:
            self.log.error("can not determine base snapshot name to remove on %s"%slave)
            raise ex.excError
        import time
        time.sleep(5)
        (ret, buff, err) = self._cmd(['snap', 'delete', self.path_short, snap], slave, info=True)
        if ret != 0:
            raise ex.excError(err)

    def sync_update(self):
        s = self.snapmirror_status(self.slave())
        if not self.can_sync(s=s):
            return
        if s['state'] == "Quiesced":
            self.log.error("update not applicable: quiesced")
            return
        if s['state'] == "Snapmirrored" and s['status'] == "Transferring":
            self.log.info("update not applicable: transfer in progress")
            return
        if s['state'] != "Snapmirrored" or s['status'] != "Idle":
            self.log.error("update not applicable: not in snapmirror idle status")
            return
        (ret, buff, err) = self.cmd_slave(['snapmirror', 'update', self.slave()+':'+self.path_short], info=True)
        if ret != 0:
            raise ex.excError

    def sync_resume(self):
        s = self.snapmirror_status(self.slave())
        if s['state'] != "Quiesced":
            self.log.info("resume not applicable: not quiesced")
            return
        (ret, buff, err) = self.cmd_slave(['snapmirror', 'resume', self.slave()+':'+self.path_short], info=True)
        if ret != 0:
            raise ex.excError

    def sync_quiesce(self):
        s = self.snapmirror_status(self.slave())
        if s['state'] == "Quiesced":
            self.log.info("already quiesced")
            return
        elif s['state'] != "Snapmirrored":
            self.log.error("Can not quiesce: volume not in Snapmirrored state")
            raise ex.excError
        if s['status'] == "Pending":
            self.log.error("Can not quiesce: volume in snapmirror Pending status")
            raise ex.excError
        (ret, buff, err) = self.cmd_slave(['snapmirror', 'quiesce', self.slave()+':'+self.path_short], info=True)
        if ret != 0:
            raise ex.excError
        self.wait_quiesce()

    def sync_break(self):
        (ret, buff, err) = self.cmd_slave(['snapmirror', 'break', self.slave()+':'+self.path_short], info=True)
        if ret != 0:
            raise ex.excError
        self.wait_break()

    def wait_quiesce(self):
        timeout = 60
        self.log.info("start waiting quiesce to finish (max %s seconds)"%(timeout*5))
        for i in range(timeout):
            s = self.snapmirror_status(self.slave())
            if s['state'] == "Quiesced" and s['status'] == "Idle":
                return
            time.sleep(5)
        self.log.error("timed out waiting for quiesce to finish")
        raise ex.excError

    def wait_break(self):
        timeout = 20
        for i in range(timeout):
            s = self.snapmirror_status(self.slave())
            if s['state'] == "Broken-off" and s['status'] == "Idle":
                return
            time.sleep(5)
        self.log.error("timed out waiting for break to finish")
        raise ex.excError

    def snapmirror_status(self, filer):
        (ret, buff, err) = self._cmd(['snapmirror', 'status'], filer, info=False)
        if ret != 0:
            raise ex.excError("can get snapmirror status from %s: %s"%(filer, err))
        key = ':'.join([filer, self.path_short])
        list = []
        for line in buff.split('\n'):
            l = line.split()
            if len(l) < 5:
                continue
            if l[2] == "Uninitialized":
                continue
            if l[0] == key or l[1] == key:
                list.append(l)
        if len(list) == 0:
            raise ex.excError("%s not found in snapmirror status"%self.path_short)
        elif len(list) == 1:
            l = list[0]
            master = l[0].split(':')[0]
            slave = l[1].split(':')[0]
            return dict(master=master, slave=slave, state=l[2], lag=l[3], status=l[4])
        else:
            raise ex.excError("%s is in an unsupported state. Please repair manually."%filer)

    def local_snapmirror_status(self):
        return self.snapmirror_status(self.local())

    def start(self):
        if self.local() == self.master():
            self.log.info("%s is already replication master"%self.local())
            return
        s = self.snapmirror_status(self.slave())
        if s['state'] != "Broken-off":
            try:
                self.sync_quiesce()
            except:
                if self.svc.options.force:
                    self.log.warning("force mode is on. bypass failed quiesce.")
                    pass
                else:
                    self.log.error("set force mode to bypass")
                    raise ex.excError
            self.sync_break()
        if rcEnv.node_env == "PRD":
            self.sync_swap()

    def stop(self):
        pass

    def sync_status(self, verbose=False):
        try:
            s = self.snapmirror_status(self.slave())
        except ex.excError as e:
            self.status_log(str(e))
            return rcStatus.WARN
        if s['state'] == "Snapmirrored":
            if "Transferring" in s['status']:
                self.log.debug("snapmirror transfer in progress")
                return rcStatus.WARN
            elif self.lagged(s['lag']):
                self.log.debug("snapmirror lag beyond sync_max_delay")
                return rcStatus.WARN
            else:
                return rcStatus.UP
        return rcStatus.DOWN

    def __init__(self,
                 rid=None,
                 filers={},
                 path=None,
                 user=None,
                 **kwargs):
        resSync.Sync.__init__(self,
                              rid=rid,
                              type="sync.netapp",
                              **kwargs)
        self.label = "netapp %s on %s"%(path, ', '.join(filers.values()))
        self.filers = filers
        self.path = path
        self.user = user
        self.path_short = self.path.replace('/vol/','')

    def __str__(self):
        return "%s filers=%s user=%s path=%s" % (resSync.Sync.__str__(self),\
                self.filers, self.user, self.path)

0707010005c9fd000081b40000000000000000000000015ab2175400002064000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/resSyncNexenta.py  import os
import logging

from rcGlobalEnv import rcEnv
import rcExceptions as ex
import rcStatus
import resSync
import datetime
from rcNexenta import Nexenta

class syncNexenta(resSync.Sync):
    def can_sync(self, target=None):
        try:
            self.get_endpoints()
        except ex.excError as e:
            self.log.error(str(e))
            raise ex.excError

        if self.ts is None:
            self.get_props()
        if self.skip_sync(self.ts):
            return False
        return True

    def sync_swap(self):
        # only available from CLI ?
        pass

    def sync_update(self):
        try:
            self.get_endpoints()
        except ex.excError as e:
            self.log.error(str(e))
            raise ex.excError

        if not self.can_sync() and not self.svc.options.force:
            return
        s = self.master.autosync_get_state(self.autosync)
        if s == "disabled":
            self.log.error("update not applicable: disabled")
            return
        if s == "running":
            self.log.info("update not applicable: transfer in progress")
            return
        if s != "online":
            self.log.error("update not applicable: %s state"%s)
            return
        self.master.autosync_execute(self.autosync)
        self.log.info("autosync runner execution submitted")

    def bind(self):
        b = self.local.ssh_list_bindings()
        found = False
        for k in b:
            user, hostport = k.split('@')
            if hostport == self.remote.head:
                found = True
                break
        if found:
            self.log.info("%s head already bound"%self.remote.head)
        else:
            self.local.ssh_bind(self.remote.username, self.remote.head, self.remote.password)
            self.log.info("%s head bound"%self.remote.head)

    def unbind(self):
        b = self.local.ssh_list_bindings()
        done = False
        for k in b:
            user, hostport = k.split('@')
            if hostport != self.remote.head:
                continue
            self.local.ssh_unbind(user, hostport, '1')
            self.log.info("%s head unbound"%hostport)
            done = True
        if not done:
            self.log.info("%s head already unbound"%self.remote.head)

    def sync_resync(self):
        try:
            self.get_endpoints()
            self.bind()
            self.master.autosync_enable(self.autosync)
            self.log.info("autosync enable submitted")
        except ex.excError as e:
            self.log.error(str(e))
            raise ex.excError

    def sync_break(self):
        try:
            self.get_endpoints()
            self.unbind()
            self.master.autosync_disable(self.autosync)
            self.log.info("autosync disable submitted")
            self.wait_break()
        except ex.excError as e:
            self.log.error(str(e))
            raise ex.excError

    def wait_break(self):
        import time
        timeout = 5
        for i in range(timeout, 0, -1):
            s = self.master.autosync_get_state(self.autosync)
            if s == "disabled":
                return
            if i > 1:
                time.sleep(2)
        self.log.error("timed out waiting for disable to finish")
        raise ex.excError

    def start(self):
        try:
            self.get_endpoints()
            self.local.set_can_mount(self.path)
            self.log.info("set 'canmount = on' on %s"%self.path)
        except ex.excError as e:
            self.log.error(str(e))
            raise ex.excError

    def stop(self):
        pass

    def get_props(self):
        self.props = self.master.autosync_get_props(self.autosync)

        # timestamp format : 15:34:09,May27
        now = datetime.datetime.now()
        try:
            self.ts = datetime.datetime.strptime(str(now.year)+' '+self.props['zfs/time_started'], "%Y %H:%M:%S,%b%d")
            if now < self.ts:
                self.ts = datetime.datetime.strptime(str(now.year-1)+' '+self.props['zfs/time_started'], "%Y %H:%M:%S,%b%d")
        except ValueError:
            raise ex.excError("can not parse last sync date: %s"%self.props['zfs/time_started'])
        self.age = now - self.ts

    def _status(self, verbose=False):
        ret = rcStatus.UP
        try:
            self.get_endpoints()
            self.status_log("master head is %s"%self.master.head)
            self.get_props()
        except ex.excError as e:
            if 'message' in e.value:
                msg = e.value['message']
            else:
                msg = str(e)
            self.status_log(msg)
            return rcStatus.WARN
        except:
            self.status_log("unexpected error")
            self.save_exc()
            return rcStatus.WARN

        limit = datetime.timedelta(minutes=self.sync_max_delay)
        if self.age > limit:
            self.status_log("last sync too old: %s ago"%str(self.age))
            ret = rcStatus.WARN
        s = self.master.autosync_get_state(self.autosync)
        if s not in ['online', 'running']:
            self.status_log("runner in '%s' state"%s)
            ret = rcStatus.WARN
        if ret == rcStatus.UP:
            self.status_log("last sync %s ago"%str(self.age))
        return ret

    def get_endpoints(self):
        """ determine which head is the replication master and
            which is replication slave.
        """
        if self.local is not None and self.remote is not None:
            return

        heads = list(set(self.filers.values()) - set([self.filer]))
        if len(heads) != 1:
            raise ex.excError("two heads need to be setup")

        self.local = Nexenta(self.filer, self.log)
        self.remote = Nexenta(heads[0], self.log)

        prop = 'zfs/to-host'
        try:
            localdown = False
            props = self.local.autosync_get_props(self.autosync)
            if prop in props and props[prop] == self.filer:
                self.slave = self.local
                self.master = self.remote
            else:
                self.slave = self.remote
                self.master = self.local
            return
        except ex.excError as e:
            if 'does not exist' in str(e):
                path_props = self.local.get_props(self.path)
                if path_props is None:
                    raise ex.excError("path '%s' not found on local head '%s'"%(self.path, self.filer))
                self.slave = self.local
                self.master = self.remote
            else:
                # local head is down
                localdown = True

        try:
            props = self.remote.autosync_get_props(self.autosync)
            if prop in props and props[prop] == self.filer:
                self.slave = self.local
                self.master = self.remote
            else:
                self.slave = self.remote
                self.master = self.local
            return
        except ex.excError as e:
            if 'does not exist' in str(e):
                path_props = self.remote.get_props(self.path)
                if path_props is None:
                    raise ex.excError("path '%s' not found on remote head '%s'"%(self.path, self.filer))
                self.slave = self.remote
                self.master = self.local
            elif localdown:
                raise ex.excError("both heads unreachable")

    def __init__(self,
                 rid=None,
                 name=None,
                 path=None,
                 filers={},
                 reversible=False,
                 **kwargs):
        resSync.Sync.__init__(self,
                              rid=rid,
                              type="sync.nexenta",
                              **kwargs)
        self.label = "nexenta autosync %s"%name
        self.autosync = name
        self.filers = filers
        self.path = path
        self.reversible = reversible
        self.filer = filers[rcEnv.nodename]
        self.master = None
        self.slave = None
        self.ts = None
        self.age = None
        self.props = None
        self.local = None
        self.remote = None

    def __str__(self):
        return "%s autosync=%s" % (resSync.Sync.__str__(self),\
                self.autosync)

0707010005c9fe000081b40000000000000000000000015ab21754000028e9000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/resSyncRados.py    import json

from rcUtilities import which, justcall
import rcExceptions as ex
import rcStatus
import datetime
import resSync

class syncRadosSnap(resSync.Sync):
    def recreate(self):
        self.validate_image_fmt()
        for image in self.images:
            self._recreate(image)

    def _recreate(self, image):
        snapnames = self._get_all(image)
        last_date, last_name = self._get_last(image)
        if self.skip_sync(last_date):
            self.log.info("skip resync for image %s: last resync on %s"%(image, str(last_date)))
            return
        snapname = self.snap_basename() + datetime.datetime.now().strftime(self.date_fmt)
        cmd = self.rbd_cmd()+['snap', 'create', image, '--snap', snapname]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

        for snapname in snapnames:
            self.rm(snapname)

    def rm(self, image):
        cmd = self.rbd_cmd()+['snap', 'rm', image]
        ret, out, err = self.vcall(cmd)

    def unprotect(self, image):
        cmd = self.rbd_cmd()+['snap', 'unprotect', image]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def get_all(self):
        data = {}
        for image in self.images:
            data[image] = self._get_all(image)
        return data

    def _get_all(self, image):
        data = self.list()
        retained = []
        prefix = image+"@"+self.snap_basename()
        for name in data:
            if not name.startswith(prefix):
                continue
            retained.append(name)
        return retained

    def get_last(self):
        data = {}
        for image in self.images:
            data[image] = self._get_last(image)
        return data

    def _get_last(self, image):
        data = self.list()
        retained = []
        prefix = image+"@"+self.snap_basename()
        for name in data:
            if not name.startswith(prefix):
                continue
            try:
                date = datetime.datetime.strptime(name, prefix+self.date_fmt)
            except:
                continue
            retained.append((date, name))
        if len(retained) == 0:
            return None, None
        last_date, last_name = sorted(retained)[-1]
        return last_date, last_name

    def rbd_cmd(self):
        l = ["rbd"]
        if self.client_id:
            l += ["-n", self.client_id]
        if self.keyring:
            l += ["--keyring", self.keyring]
        return l

    def snap_basename(self):
        return self.rid+"."

    def get_pools(self):
        l = set()
        for image in self.images:
            pool = image.split("/")[0]
            l.add(pool)
        return l

    def list(self):
        if self.list_data is not None:
            return self.list_data
        data = {}
        for pool in self.get_pools():
            data.update(self._list(pool))

        self.list_data = data
        return data

    def _list(self, pool):
        cmd = self.rbd_cmd() + ["ls", "-l", pool, "--format", "json"]
        out, err, ret = justcall(cmd)
        data = {}
        try:
            _data = json.loads(out)
        except Exception as e:
            self.status_log(str(e))
            _data = []
        for img_data in _data:
            idx = pool+"/"+img_data['image']
            if "snapshot" in img_data:
                idx += "@"+img_data['snapshot']
            data[idx] = img_data
        return data

    def sync_status(self, verbose=False):
        try:
            self.validate_image_fmt()
        except Exception as e:
            self.status_log(str(e))
            return rcStatus.WARN

        try:
            data = self.get_last()
        except Exception as e:
            self.status_log(str(e))
            return rcStatus.WARN

        nosnap = []
        expired = []
        ok = []

        for image in self.images:
            date, snapname = data[image]

            if date is None:
                nosnap.append(image)
            elif date < datetime.datetime.now() - datetime.timedelta(minutes=self.sync_max_delay):
                expired.append(image)
            else:
                ok.append(image)

        r = rcStatus.UP

        if len(nosnap) > 0:
            self.status_log("no snap found for images: "+", ".join(nosnap))
            r = rcStatus.WARN
        if len(expired) > 0:
            self.status_log("snap too old for images: "+", ".join(expired))
            r = rcStatus.WARN

        return r

    def sync_update(self):
        self.recreate()

    def sync_resync(self):
        self.recreate()

    def __init__(self,
                 rid=None,
                 images=[],
                 client_id=None,
                 keyring=None,
                 **kwargs):
        resSync.Sync.__init__(self,
                              rid=rid,
                              type="sync.radossnap",
                              **kwargs)

        self.fmt_label("snap", images)
        self.images = images
        if not client_id.startswith("client."):
            client_id = "client."+client_id
        self.client_id = client_id
        self.keyring = keyring
        self.list_data = None
        self.date_fmt = "%Y-%m-%d.%H:%M:%S"

    def validate_image_fmt(self):
        l = []
        for image in self.images:
            if image.count("/") != 1:
                l.append(image)
        if len(l) > 0:
            raise ex.excError("wrong format (expected pool/image): "+", ".join(l))

    def fmt_label(self, t, l):
        self.label = t+" rados %s"%', '.join(l)
        if len(self.label) > 80:
            self.label = self.label[:76]+"..."

    def __str__(self):
        return "%s images=%s" % (resSync.Sync.__str__(self),\
                ', '.join(self.images))

class syncRadosClone(syncRadosSnap):
    def __init__(self,
                 rid=None,
                 pairs=[],
                 client_id=None,
                 keyring=None,
                 type="sync.radosclone",
                 sync_max_delay=None,
                 schedule=None,
                 optional=False,
                 disabled=False,
                 tags=set(),
                 internal=False,
                 subset=None):
        images = map(lambda x: x.split(":")[0], pairs)
        syncRadosSnap.__init__(self,
                              rid=rid,
                              images=images,
                              client_id=client_id,
                              keyring=keyring,
                              type=type,
                              sync_max_delay=sync_max_delay,
                              schedule=schedule,
                              optional=optional,
                              disabled=disabled,
                              tags=tags,
                              subset=subset)

        self.pairs = pairs
        self.fmt_label("clone", pairs)

    def recreate(self):
        self.validate_pair_fmt()
        for pair in self.pairs:
            self._recreate(pair)

    def _recreate(self, pair):
        image, clone = pair.split(":")
        snapnames = self._get_all(image)
        last_date, last_name = self._get_last(image)
        if self.skip_sync(last_date):
            self.log.info("skip resync for image %s: last resync on %s"%(image, str(last_date)))
            return
        snapname = self.snap_basename() + datetime.datetime.now().strftime(self.date_fmt)
        cmd = self.rbd_cmd()+['snap', 'create', image, '--snap', snapname]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError
        cmd = self.rbd_cmd()+['snap', 'protect', image+"@"+snapname]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

        list_data = self.list()
        if clone in list_data:
            cmd = self.rbd_cmd()+['rm', clone]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                raise ex.excError

        cmd = self.rbd_cmd()+['clone', image+"@"+snapname, clone]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

        for snapname in snapnames:
            try:
                self.unprotect(snapname)
                self.rm(snapname)
            except:
                pass

    def validate_pair_fmt(self):
        l = []
        for pair in self.pairs:
            try:
                image, clone = pair.split(":")
            except:
                l.append(image)
                continue
            if image.count("/") != 1 or clone.count("/") != 1:
                l.append(image)
        if len(l) > 0:
            raise ex.excError("wrong format (expected pool/image:pool/image): "+", ".join(l))

    def snap_basename(self):
        return self.rid+".cloneref."

    def _status(self, verbose=False):
        try:
            self.validate_pair_fmt()
        except Exception as e:
            self.status_log(str(e))
            return rcStatus.WARN

        try:
            data = self.get_last()
        except Exception as e:
            self.status_log(str(e))
            return rcStatus.WARN

        nosnap = []
        noclone = []
        expired = []
        invclone = []
        ok = []

        for image in self.images:
            date, snapname = data[image]

            if date is None:
                nosnap.append(image)
            elif date < datetime.datetime.now() - datetime.timedelta(minutes=self.sync_max_delay):
                expired.append(image)
            else:
                ok.append(image)

        list_data = self.list()
        for pair in self.pairs:
            image, clone = pair.split(":")
            if clone not in list_data:
                noclone.append(pair)
            elif not list_data[clone].get("parent"):
                invclone.append(pair)

        r = rcStatus.UP

        if len(nosnap) > 0:
            self.status_log("no snap found for images: "+", ".join(nosnap))
            r = rcStatus.WARN
        if len(expired) > 0:
            self.status_log("snap too old for images: "+", ".join(expired))
            r = rcStatus.WARN
        if len(noclone) > 0:
            self.status_log("no clone found for pairs: "+", ".join(noclone))
            r = rcStatus.WARN
        if len(invclone) > 0:
            self.status_log("clone invalid for pairs: "+", ".join(invclone))
            r = rcStatus.WARN

        return r
   0707010005c9ff000081b40000000000000000000000015ab217540000408a000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/resSyncRsync.py    import os
import logging
import glob

from rcGlobalEnv import rcEnv
from rcUtilities import which, justcall, lazy, cache, bdecode
from converters import convert_speed, convert_size
import rcExceptions as ex
import rcStatus
import datetime
import resSync

def lookup_snap_mod():
    if rcEnv.sysname == 'Linux':
        return __import__('snapLvmLinux')
    elif rcEnv.sysname == 'HP-UX':
        return __import__('snapVxfsHP-UX')
    elif rcEnv.sysname == 'AIX':
        return __import__('snapJfs2AIX')
    elif rcEnv.sysname in ['SunOS', 'FreeBSD']:
        return __import__('snapZfsSunOS')
    elif rcEnv.sysname in ['OSF1']:
        return __import__('snapAdvfsOSF1')
    else:
        raise ex.excError

def get_timestamp_filename(self, node):
    sync_timestamp_f = os.path.join(self.var_d, "last_sync_"+node)
    return sync_timestamp_f

def add_sudo_rsync_path(options):
    if "--rsync-path" not in " ".join(options):
        options += ['--rsync-path', 'sudo rsync']
        return options

    new = []
    skip = False
    for i, w in enumerate(options):
        if skip:
            skip = False
            continue
        if w.startswith('--rsync-path'):
            if "=" in w:
                l = w.split("=")
                if len(l) == 2:
                    val = l[1]
            elif len(options) > i+1:
                val = options[i+1]
                skip = True
            else:
                raise ex.excError("malformed --rsync-path value")
            if not "sudo " in val:
                val = val.strip("'")
                val = val.strip('"')
                val = "sudo "+val
            new += ['--rsync-path', val]
        else:
            new.append(w)
    return new

def get_timestamp(self, node):
    ts = None
    sync_timestamp_f = get_timestamp_filename(self, node)
    if not os.path.exists(sync_timestamp_f):
        return None
    try:
        with open(sync_timestamp_f, 'r') as f:
            d = f.read()
            ts = datetime.datetime.strptime(d,"%Y-%m-%d %H:%M:%S.%f\n")
            f.close()
    except:
        self.log.info("failed get last sync date for %s to %s"%(self.src, node))
        return ts
    return ts

class Rsync(resSync.Sync):
    """Defines a rsync job from local node to its remote nodes. Target nodes
    can be restricted to production sibblings or to disaster recovery nodes,
    or both.
    """
    def node_can_sync(self, node):
        ts = get_timestamp(self, node)
        return not self.skip_sync(ts)

    def node_need_sync(self, node):
        ts = get_timestamp(self, node)
        return self.alert_sync(ts)

    def can_sync(self, target=None):
        targets = set()
        if target is None:
            targets = self.nodes_to_sync('nodes')
            targets |= self.nodes_to_sync('drpnodes')
        else:
            targets = self.nodes_to_sync(target)

        if len(targets) == 0:
            return False
        return True

    def nodes_to_sync(self, target=None, state="syncable", status=False):
        # Checks are ordered by cost
        if self.is_disabled():
            return set()

        # DRP nodes are not allowed to sync nodes nor drpnodes
        if rcEnv.nodename in self.svc.drpnodes:
            return set()

        self.pre_sync_check_flex_primary()

        if target in self.target:
            targets = self.target_nodes(target)
        else:
            return set()

        # Discard the local node from the set
        targets -= set([rcEnv.nodename])

        if len(targets) == 0:
            return set()

        for node in targets.copy():
            if state == "syncable" and not self.node_can_sync(node):
                targets.remove(node)
                continue
            elif state == "late" and not self.node_need_sync(node):
                targets.remove(node)
                continue

        if len(targets) == 0:
            return set()

        #
        # Accept to sync from here only if the service is up
        # Also accept n/a status, because it's what the avail status
        # ends up to be when only sync#* are specified using --rid
        #
        s = self.svc.group_status(excluded_groups=set(["sync", "hb", "app"]))
        if not self.svc.options.force and \
           s['avail'].status not in [rcStatus.UP, rcStatus.NA]:
            if s['avail'].status == rcStatus.WARN:
                if not self.svc.options.cron:
                    self.log.info("won't sync this resource service in warn status")
            elif not self.svc.options.cron:
                self.log.info("won't sync this resource for a service not up")
            return set()

        for node in targets.copy():
            if not status and not self.remote_fs_mounted(node):
                targets.remove(node)
                continue

        return targets

    def bwlimit_option(self):
        if self.bwlimit is not None:
            bwlimit = [ '--bwlimit='+str(self.bwlimit) ]
        elif self.svc.bwlimit is not None:
            bwlimit = [ '--bwlimit='+str(self.svc.bwlimit) ]
        else:
            bwlimit = []
        return bwlimit

    def mangle_options(self, ruser):
        options = [] + self.full_options
        if ruser != "root":
            options = add_sudo_rsync_path(options)
        options += self.bwlimit_option()
        if '-e' in options:
            return options

        if rcEnv.rsh.startswith("/usr/bin/ssh") and rcEnv.sysname == "SunOS":
            # SunOS "ssh -n" doesn't work with rsync
            rsh = rcEnv.rsh.replace("-n", "")
        else:
            rsh = rcEnv.rsh
        options += ['-e', rsh]
        return options

    def sync_timestamp(self, node):
        sync_timestamp_f = get_timestamp_filename(self, node)
        sync_timestamp_f_src = get_timestamp_filename(self, rcEnv.nodename)
        sched_timestamp_f = os.path.join(self.svc.var_d, "scheduler", "last_syncall_"+self.rid)
        dst_d = os.path.dirname(sched_timestamp_f)
        if not os.path.exists(dst_d):
            os.makedirs(dst_d)
        with open(sync_timestamp_f, 'w') as f:
            f.write(str(self.svc.action_start_date)+'\n')
        import shutil
        shutil.copy2(sync_timestamp_f, sync_timestamp_f_src)
        shutil.copy2(sync_timestamp_f, sched_timestamp_f)
        tsfiles = glob.glob(os.path.join(self.var_d, "last_sync_*"))
        ruser = self.svc.node.get_ruser(node)
        options = self.mangle_options(ruser)
        cmd = ['rsync'] + options
        cmd += ['-R'] + tsfiles + [ruser+'@'+node+':/']
        self.call(cmd)

    def sync(self, target):
        self.add_resource_files_to_sync()
        if target not in self.target:
            if not self.svc.options.cron:
                self.log.info('%s => %s sync not applicable to %s', 
                              " ".join(self.src), self.dst, target)
            return 0

        targets = self.nodes_to_sync(target)

        if len(targets) == 0:
            raise ex.syncNoNodesToSync

        if "delay_snap" in self.tags:
            if not hasattr(self.rset, 'snaps'):
                Snap = lookup_snap_mod()
                self.rset.snaps = Snap.Snap(self.rid)
                self.rset.snaps.set_logger(self.log)
            self.rset.snaps.try_snap(self.rset, target, rid=self.rid)

        if hasattr(self, "alt_src"):
            """ The pre_action() has provided us with a better source
                to sync from. Use that
            """
            src = self.alt_src
        else:
            src = self.src

        if len(src) == 0:
            if not self.svc.options.cron:
                self.log.info("no files to sync")
            raise ex.syncNoFilesToSync

        for node in targets:
            ruser = self.svc.node.get_ruser(node)
            dst = ruser + '@' + node + ':' + self.dst
            options = self.mangle_options(ruser)
            cmd = ['rsync'] + options + src
            cmd.append(dst)
            if self.rid.startswith("sync#i"):
                ret, out, err = self.call(cmd)
            else:
                ret, out, err = self.vcall(cmd)
            if ret != 0:
                self.log.error("node %s synchronization failed (%s => %s)" % (node, src, dst))
                continue
            self.sync_timestamp(node)
            self.svc.need_postsync |= set([node])
            stats = self.parse_rsync(out)
            self.update_stats(stats, target=node)

        self.write_stats()

    def parse_rsync(self, buff):
        """
        Extract normalized speed and transfered data size from the dd output
        """
        data = {"bytes": 0, "speed": 0}
        for line in bdecode(buff).splitlines():
            if line.startswith("Total bytes sent"):
                data["bytes"] = int(line.split()[-1].replace(",",""))
            elif line.endswith("/sec"):
                data["speed"] = int(convert_speed(line.split()[-2].replace(",","")+"/s"))
        return data

    def pre_action(self, action):
        """Actions to do before resourceSet iterates through the resources to
           trigger action() on each one
        """

        resources = [r for r in self.rset.resources if \
                     not r.skip and not r.is_disabled() and \
                     r.type == self.type]

        if len(resources) == 0:
            return

        self.pre_sync_check_prd_svc_on_non_prd_node()

        """ Is there at least one node to sync ?
        """
        targets = set()
        rtargets = {0: set()}
        need_snap = False
        for i, r in enumerate(resources):
            if r.skip or r.is_disabled():
                continue
            rtargets[i] = set()
            if action == "sync_nodes":
                rtargets[i] |= r.nodes_to_sync('nodes')
            else:
                rtargets[i] |= r.nodes_to_sync('drpnodes')
            for node in rtargets[i].copy():
                if not r.node_can_sync(node):
                    rtargets[i] -= set([node])
                elif r.snap:
                    need_snap = True
        for i in rtargets:
            targets |= rtargets[i]

        if len(targets) == 0:
            if not self.svc.options.cron:
                self.rset.log.info("no nodes to sync")
            raise ex.excAbortAction

        if not need_snap:
            self.rset.log.debug("snap not needed")
            return

        Snap = lookup_snap_mod()
        try:
            self.rset.snaps = Snap.Snap(self.rid)
            self.rset.snaps.set_logger(self.rset.log)
            self.rset.snaps.try_snap(self.rset, action)
        except ex.syncNotSnapable:
            raise ex.excError

    def post_action(self, action):
        """Actions to do after resourceSet has iterated through the resources to
           trigger action() on each one
        """
        resources = [r for r in self.rset.resources if \
                     not r.skip and not r.is_disabled() and \
                     r.type == self.type]

        if len(self.rset.resources) == 0:
            return

        if hasattr(self.rset, 'snaps'):
            self.rset.snaps.snap_cleanup(self.rset)

    def sync_nodes(self):
        try:
            self.sync("nodes")
        except ex.syncNoFilesToSync:
            if not self.svc.options.cron:
                self.log.info("no files to sync")
            pass
        except ex.syncNoNodesToSync:
            if not self.svc.options.cron:
                self.log.info("no nodes to sync")
            pass

    def sync_drp(self):
        try:
            self.sync("drpnodes")
        except ex.syncNoFilesToSync:
            if not self.svc.options.cron:
                self.log.info("no files to sync")
            pass
        except ex.syncNoNodesToSync:
            if not self.svc.options.cron:
                self.log.info("no nodes to sync")
            pass

    def sync_status(self, verbose=False):
        """ mono-node service should return n/a as a sync state
        """
        target = set()
        for i in self.target:
            target |= self.target_nodes(i)
        if len(target - set([rcEnv.nodename])) == 0:
            self.status_log("no destination nodes", "info")
            return rcStatus.NA

        try:
            options = [] + self.full_options
        except ex.excError as e:
            self.status_log(str(e))
            return rcStatus.WARN

        """ sync state on nodes where the service is not UP
        """
        s = self.svc.group_status(excluded_groups=set(["sync", "hb", "app"]))
        if s['avail'].status != rcStatus.UP or \
           (self.svc.topology == 'flex' and \
            rcEnv.nodename != self.svc.flex_primary and \
            s['avail'].status == rcStatus.UP):
            if rcEnv.nodename not in target:
                self.status_log("passive node not in destination nodes", "info")
                return rcStatus.NA
            if self.node_need_sync(rcEnv.nodename):
                self.status_log("passive node needs update")
                return rcStatus.WARN
            else:
                return rcStatus.UP

        """ sync state on DRP nodes where the service is UP
        """
        if 'drpnodes' in self.target and rcEnv.nodename in self.target_nodes('drpnodes'):
            self.status_log("service up on drp node, sync disabled", "info")
            return rcStatus.NA

        """ sync state on nodes where the service is UP
        """
        nodes = []
        nodes += self.nodes_to_sync('nodes', state="late", status=True)
        nodes += self.nodes_to_sync('drpnodes', state="late", status=True)
        if len(nodes) == 0:
            return rcStatus.UP

        self.status_log("%s need update"%', '.join(nodes))
        return rcStatus.DOWN

    @cache("rsync.version")
    def rsync_version(self):
        if which("rsync") is None:
            raise ex.excError("rsync not found")
        cmd = ['rsync', '--version']
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.excError("can not determine rsync capabilities")
        return out

    @lazy
    def full_options(self):
        baseopts = '-HAXpogDtrlvx'
        out = self.rsync_version()
        if 'no xattrs' in out:
            baseopts = baseopts.replace('X', '')
        if 'no ACLs' in out:
            baseopts = baseopts.replace('A', '')
        options = [baseopts, '--stats', '--delete', '--force', '--timeout='+str(self.timeout)] + self.options
        return options

    def __init__(self,
                 rid=None,
                 src=[],
                 dst=None,
                 options=[],
                 target=[],
                 dstfs=None,
                 snap=False,
                 bwlimit=None,
                 internal=False,
                 **kwargs):
        resSync.Sync.__init__(self,
                              rid=rid,
                              type="sync.rsync",
                              **kwargs)

        if internal:
            if rcEnv.paths.drp_path in dst:
                self.label = "rsync system files to drpnodes"
            else:
                self.label = "rsync svc config to %s"%(', '.join(sorted(target)))
        else:
            _src = ', '.join(src)
            if len(_src) > 300:
                _src = _src[0:300]
            _dst = ', '.join(target)
            self.label = "rsync %s to %s"%(_src, _dst)
        self.src = src
        self.dst = dst
        self.dstfs = dstfs
        self.snap = snap
        self.target = target
        self.bwlimit = bwlimit
        self.internal = internal
        self.timeout = 3600
        self.options = options

    def add_resource_files_to_sync(self):
        if self.rid != "sync#i0":
            return
        for resource in self.svc.get_resources():
            self.src += resource.files_to_sync()

    def _info(self):
        self.add_resource_files_to_sync()
        data = [
          ["src", " ".join(self.src)],
          ["dst", self.dst],
          ["dstfs", self.dstfs if self.dstfs else ""],
          ["bwlimit", self.bwlimit if self.bwlimit else ""],
          ["snap", str(self.snap).lower()],
          ["timeout", str(self.timeout)],
          ["target", " ".join(sorted(self.target))],
          ["options", " ".join(self.options)],
        ]
        data += self.stats_keys()
        return data

    def __str__(self):
        return "%s src=%s dst=%s options=%s target=%s" % (resSync.Sync.__str__(self),\
                self.src, self.dst, str(self.full_options), str(self.target))

  0707010005ca00000081b40000000000000000000000015ab2175400002463000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/resSyncS3.py   import os

from rcGlobalEnv import rcEnv
from rcUtilities import which, justcall
from subprocess import *
import rcExceptions as ex
import rcStatus
import time
import datetime
import resSync
import glob

class syncS3(resSync.Sync):
    def __init__(self,
                 rid=None,
                 src=[],
                 options=[],
                 bucket=None,
                 snar=None,
                 full_schedule="@1441 sun",
                 **kwargs):
        resSync.Sync.__init__(self, rid=rid, type="sync.s3", **kwargs)
        self.label = "s3 backup"
        self.src = src
        self.bucket = bucket
        self.options = options
        self.full_schedule = full_schedule
        self.snar = snar

    def on_add(self):
        self.prefix = "/" + self.svc.svcname + "/" + self.rid.replace("#",".")
        dst = "s3://"+self.bucket + self.prefix
        self.label += " to " + dst
        if self.snar is None:
            self.snar = os.path.join(self.var_d, self.rid.replace("#", "."))+".snar"

    def sync_basename(self, n):
        return os.path.basename(self.sync_fullname(n))

    def sync_fullname(self, n):
        s = self.prefix
        if n > 0:
            s += ".incr"+str(n)
        s += ".tar.gz"
        return s

    def sync_date(self, n):
        key = self.sync_basename(n)
        try:
            e = [ d for d in self.ls() if d["key"] == key ][0]
        except:
            raise ex.excError("key %s not found in bucket" % key)
        try:
            _d = datetime.datetime.strptime(e["date"], "%Y-%m-%d %H:%M:%S")
        except:
            raise ex.excError("undecodable date %s" % e["date"])
        return _d

    def _status(self, verbose=False):
        try:
            self.check_bin()
        except ex.excError as e:
            self.status_log(str(e))
            return rcStatus.WARN
        try:
            l = self.ls(refresh=True)
            n = self.get_n_incr()
        except Exception as e:
            self.status_log(str(e))
            return rcStatus.WARN

        if n is None:
            self.status_log("no backup found")
            return rcStatus.WARN

        if n > 0 and not os.path.exists(self.snar):
            self.status_log("snar file not found at %s" % self.snar)
            return rcStatus.WARN

        try:
            last = self.sync_date(n)
        except Exception as e:
            self.status_log(str(e))
            return rcStatus.WARN

        if self.sync_date(n) < datetime.datetime.now() - datetime.timedelta(minutes=self.sync_max_delay):
            self.status_log("last backup too old (%s)" % last.strftime("%Y-%m-%d %H:%M:%S"))
            return rcStatus.WARN

        self.status_log("last backup on %s" % last.strftime("%Y-%m-%d %H:%M:%S"))
        return rcStatus.UP

    def check_bin(self):
        if not which("gof3r"):
            raise ex.excError("could not find gof3r binary")
        if not which("tar"):
            raise ex.excError("could not find tar binary")

    def sync_full(self):
        self.check_bin()
        self.tar_full()

    def sync_update(self):
        self.check_bin()
        self.tar()

    def ls(self, refresh=False):
        """
          list all saves in S3 for this resource
        """
        if not refresh and hasattr(self, "ls_cache"):
            return self.ls_cache
        cmd = ["aws", "s3", "ls", "s3://"+self.bucket+"/"+self.svc.svcname+"/"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return []
        l = []
        for line in out.split("\n"):
            v = line.split()
            if len(v) != 4:
                continue
            if v[2] == "PRE":
                continue
            if not v[-1].startswith(self.rid.replace("#", ".")):
                continue
            d = {
              'date': " ".join(v[:2]),
              'key': v[-1],
            }
            l.append(d)
        self.ls_cache = l
        return self.ls_cache

    def get_creds_from_aws(self):
        import ConfigParser
        aws_cf_f = "/root/.aws/config"
        try:
            aws_cf = ConfigParser.RawConfigParser()
            aws_cf.read(aws_cf_f)
        except:
            raise ex.excError("failed to load aws config at %s" % aws_cf_f)
        if hasattr(self.svc, "aws_profile"):
            profile = self.svc.aws_profile
        else:
            profile = "default"
        try:
            key = aws_cf.get(profile, "aws_access_key_id")
        except:
            raise ex.excError("aws_access_key_id not found in section %s of %s" % (profile, aws_cf_f))
        try:
            secret = aws_cf.get(profile, "aws_secret_access_key")
        except:
            raise ex.excError("aws_secret_access_key not found in section %s of %s" % (profile, aws_cf_f))
        return key, secret

    def set_creds(self):
        key, secret = self.get_creds_from_aws()
        os.environ["AWS_ACCESS_KEY_ID"] = key
        os.environ["AWS_SECRET_ACCESS_KEY"] = secret

    def unset_creds(self):
        if "AWS_ACCESS_KEY_ID" in os.environ:
            del(os.environ["AWS_ACCESS_KEY_ID"])
        if "AWS_SECRET_ACCESS_KEY" in os.environ:
            del(os.environ["AWS_SECRET_ACCESS_KEY"])

    def get_n_incr(self):
        l = self.ls()
        keys = sorted([d["key"] for d in l])
        n_incr = None
        full_found = False
        for i in range(len(keys)):
            last = keys[-(i+1)]
            if last == self.rid.replace("#", ".") + ".tar.gz":
                full_found = True
            v = last.split(".")
            if len(v) < 3:
                continue
            if v[-1] == "tar":
                incr = v[-2]
            elif v[-2] == "tar":
                incr = v[-3]
            else:
                continue
            if not incr.startswith("incr"):
                continue
            incr = incr.replace("incr", "")
            try:
                n_incr = int(incr)
                return n_incr
            except:
                continue
        if full_found:
            return 0
        return n_incr

    def remove_incr(self):
        cmd = ["aws", "s3", "rm"]
        keys = [ d["key"] for d in self.ls() ]
        for key in keys:
            if not key.startswith(os.path.basename(self.prefix) + ".incr"):
                continue
            self.vcall(cmd + ["s3://"+self.bucket+os.path.dirname(self.prefix)+"/"+key])

    def in_full_schedule(self):
        from rcScheduler import Scheduler, SchedNotAllowed, SchedSyntaxError
        sched = Scheduler()
        schedule = sched.sched_get_schedule("dummy", "dummy", schedules=self.full_schedule)
        try:
            sched.in_schedule(schedule, now=datetime.datetime.now())
        except SchedNotAllowed:
            return False
        except SchedSyntaxError as e:
            raise ex.excError(str(e))
        return True

    def tar(self):
        n_incr = self.get_n_incr()
        if n_incr is None:
            self.log.info("first backup")
            self.tar_full()
        elif self.in_full_schedule():
            self.log.info("in schedule for a full backup")
            self.tar_full()
        else:
            self.tar_incr(n_incr+1)

    def tar_full(self):
        if os.path.exists(self.snar):
            self.log.info("full backup, removing snar file")
            self.log.info("rm " + self.snar)
            os.unlink(self.snar)
        else:
            self.log.info("full backup, no snar file found at %s" % self.snar)
        self.do_tar()
        self.remove_incr()

    def tar_incr(self, n):
        if os.path.exists(self.snar):
            self.log.info("incremental backup, using snar file")
        else:
            self.log.info("full backup, no snar file found at %s" % self.snar)
        self.do_tar(n=n)

    def do_tar(self, n=None):
        self.set_creds()
        paths = []
        for e in self.src:
            paths += glob.glob(e)
        cmd1 = ["tar", "czf", "-", "-g", self.snar] + self.options + paths
        p1 = Popen(cmd1, stdout=PIPE, stderr=PIPE)
        cmd2 = ["gof3r", "put", "-b", self.bucket, "-k", self.sync_fullname(n)]
        p2 = Popen(cmd2, stdin=p1.stdout, stdout=PIPE, stderr=PIPE)
        self.log.info(" ".join(cmd1) + " | " + " ".join(cmd2))
        out, err = p2.communicate()
        self.unset_creds()
        if len(out) > 0:
            self.log.info(out)
        if p2.returncode != 0:
            if len(err) > 0:
                self.log.error(err)

    def do_tar_x(self, n=None):
        self.set_creds()
        paths = []
        cmd1 = ["gof3r", "get", "-b", self.bucket, "-k", self.sync_fullname(n)]
        p1 = Popen(cmd1, stdout=PIPE, stderr=PIPE)
        cmd2 = ["tar", "xzf", "-", "-g", self.snar, "-C", "/"]
        p2 = Popen(cmd2, stdin=p1.stdout, stdout=PIPE, stderr=PIPE)
        self.log.info(" ".join(cmd1) + " | " + " ".join(cmd2))
        out, err = p2.communicate()
        self.unset_creds()
        if len(out) > 0:
            self.log.info(out)
        if p2.returncode != 0:
            if len(err) > 0:
                self.log.error(err)

    def sync_restore(self):
        n = self.get_n_incr()
        for i in range(n):
            self.do_tar_x(i)

    def __str__(self):
        return "%s src=%s bucket=%s" % (resSync.Sync.__str__(self), str(self.src), str(self.bucket))

 0707010005ca01000081b40000000000000000000000015ab217540000540f000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/resSyncSymSrdfS.py import os
import logging
import json

from rcGlobalEnv import rcEnv
from rcUtilities import which, lazy
from xml.etree.ElementTree import ElementTree, XML

import rcExceptions as ex
import rcStatus
import time
import datetime
import resSync

os.environ['PATH'] += ":/usr/symcli/bin"
os.environ['SYMCLI_WAIT_ON_DB'] = "1"
os.environ['SYMCLI_WAIT_ON_GK'] = "1"

class syncSymSrdfS(resSync.Sync):

    def list_pd(self):
        """
        <?xml version="1.0" standalone="yes" ?>
        <SymCLI_ML>
          <Inquiry>
            <Dev_Info>
              <pd_name>/dev/sdb</pd_name>
              <dev_name>000F1</dev_name>
              <symid>000196801561</symid>
              <dev_ident_name>V_TOOLSDL360S24</dev_ident_name>
            </Dev_Info>
            <Product>
              <vendor>EMC</vendor>
            </Product>
          </Inquiry>
        """
        inq = {}
        cmd = ["syminq", "-identifier", "device_name", "-output", "xml_e"]
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            raise ex.excError("Failed to run command %s"% ' '.join(cmd) )
        xml = XML(out)
        for e in xml.findall("Inquiry/Dev_Info"):
            pd_name = e.find("pd_name").text
            dev_name = e.find("dev_name").text
            if dev_name not in inq:
                inq[dev_name] = []
            inq[dev_name].append(pd_name)

        """
        <?xml version="1.0" standalone="yes" ?>
        <SymCLI_ML>
          <DG>
            <Device>
              <Dev_Info>
                <dev_name>003AD</dev_name>
                <configuration>RDF1+TDEV</configuration>
                <ld_name>DEV001</ld_name>
                <status>Ready</status>
              </Dev_Info>
              <Front_End>
                <Port>
                  <pd_name>/dev/sdq</pd_name>
                  <director>07E</director>
                  <port>1</port>
                </Port>
              </Front_End>
            </Device>
          </DG>
        </SymCLI_ML>
        """
        cmd = ['symdg', '-g', self.symdg, 'list', 'ld', '-output', 'xml_e',
               '-i', '15', '-c', '4']
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            raise ex.excError("Failed to run command %s"% ' '.join(cmd) )
        devs = []
        xml = XML(out)
        for e in xml.findall("DG/Device/Dev_Info"):
            dev_name = e.find("dev_name").text
            if dev_name in inq:
                devs += inq[dev_name]

        return devs

    def promote_devs_rw(self):
        if rcEnv.sysname != "Linux":
            return
        for dev in self.list_pd():
            self.promote_dev_rw(dev)

    def promote_dev_rw(self, dev):
        from rcUtilitiesLinux import promote_dev_rw
        promote_dev_rw(dev, log=self.log)

    def get_symid_from_export(self, cf):
        with open(cf, 'r') as f:
            buff = f.read()
        return buff.split("\n")[0].split()[-1]

    def postsync(self):
        local_export_symid = self.get_symid_from_export(self.dgfile_local_name)
        if local_export_symid == self.symid:
            return self.do_dgimport(self.dgfile_local_name)
        remote_export_symid = self.get_symid_from_export(self.dgfile_rdf_name)
        if remote_export_symid == self.symid:
            self.do_dgimport(self.dgfile_rdf_name)

    def presync(self):
        s = self.svc.group_status(excluded_groups=set(["sync", "app"]))
        if self.svc.options.force or s['avail'].status == rcStatus.UP:
            self.do_rdf_dgexport()
            self.do_local_dgexport()
            self.do_dg_wwn_map()

    def files_to_sync(self):
        return [
            self.dgfile_rdf_name,
            self.dgfile_local_name,
            self.wwn_map_fpath,
        ]

    @lazy
    def wwn_map_fpath(self):
        return os.path.join(self.var_d, "wwn_map")

    def do_dg_wwn_map(self):
        devs = []
        with open(self.dgfile_local_name, "r") as filep:
            for line in filep.readlines():
                if "DEV" not in line:
                    continue
                devs.append(line.split()[1])
        cmd = ["/usr/symcli/bin/symdev", "list", "-output", "xml_e", "-sid", self.symid, "-devs", ",".join(devs), "-v"]
        ret, out, err = self.call(cmd)
        if ret != 0:
            raise ex.excError("Failed to run command %s"% ' '.join(cmd) )
        tree = XML(out)
        mapping = []
        for dev in tree.findall("Symmetrix/Device"):
            try:
                local = dev.find('Product/wwn').text
                remote = dev.find('RDF/Remote/wwn').text
            except Exception as exc:
                self.log.warning(str(exc))
            else:
                mapping.append((local, remote))
        with open(self.wwn_map_fpath, 'w') as filep:
            json.dump(mapping, filep)
            filep.write("\n")

    def do_local_dgexport(self, fpath=None):
        if fpath is None:
            fpath = self.dgfile_local_name
        try:
            os.unlink(fpath)
        except:
            pass
        cmd = ['/usr/symcli/bin/symdg', 'export', self.symdg, '-f', fpath,
               '-i', '15', '-c', '4']
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            raise ex.excError("Failed to run command %s"% ' '.join(cmd) )
        return out

    def do_rdf_dgexport(self):
        fpath = self.dgfile_rdf_name
        try:
            os.unlink(fpath)
        except:
            pass
        cmd = ['/usr/symcli/bin/symdg', 'export', self.symdg, '-f', fpath,
               '-rdf', '-i', '15', '-c', '4']
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            raise ex.excError("Failed to run command %s"% ' '.join(cmd) )
        return out

    def do_dgremove(self):
        cmd = ['/usr/symcli/bin/symdg', 'delete', self.symdg, '-force',
               '-i', '15', '-c', '4']
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError("Failed to run command %s"% ' '.join(cmd) )
        return out

    def is_dgimport_needed(self):
        self.do_local_dgexport(fpath=self.dgfile_tmp_local_name)
        import filecmp
        if filecmp.cmp(self.dgfile_tmp_local_name, self.dgfile_rdf_name, shallow=False):
            return False
        return True

    def do_dgimport(self, ef):
        if self.symdg in self.get_dg_list():
            if not self.is_dgimport_needed():
                self.log.info("symrdf dg %s is already up to date"%self.symdg)
                return
            else:
                self.do_dgremove()
        self.log.info("symrdf dg %s will be imported from file"%self.symdg)
        cmd = ['symdg', 'import', self.symdg, '-f', ef, '-i', '15', '-c', '4']
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError("Failed to run command %s"% ' '.join(cmd) )
        return out

    @lazy
    def dgfile_tmp_local_name(self):
        return os.path.join(self.var_d, 'symrdf_' + self.symdg + '.dg.tmp.local')

    @lazy
    def dgfile_local_name(self):
        return os.path.join(self.var_d, 'symrdf_' + self.symdg + '.dg.local')

    @lazy
    def dgfile_rdf_name(self):
        return os.path.join(self.var_d, 'symrdf_' + self.symdg + '.dg.rdf')

    def flush_cache(self):
        self.unset_lazy("rdf_query")

    def get_symdevs(self):
        for symdev in self.symdevs:
            l = symdev.split(':')
            if len(l) != 2:
                self.log.error("symdevs must be in symid:symdev ... format")
                raise ex.excError
            self.symdev[l[0],l[1]] = dict(symid=l[0], symdev=l[1])

    @lazy
    def rdf_query(self):
        cmd = ['/usr/symcli/bin/symrdf', '-g', self.symdg, '-rdfg',
               str(self.rdfg), 'query', '-output', 'xml_e']
        ret, out, err = self.call(cmd)
        if ret != 0:
            raise ex.excError("Failed to run command %s"% ' '.join(cmd))
        return out

    def dg_query(self):
        cmd = ['/usr/symcli/bin/symdg', 'list', '-output', 'xml_e',
               '-i', '15', '-c', '4']
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            raise ex.excError("Failed to run command %s"% ' '.join(cmd) )
        return out

    # browse local device groups and build dict with list
    def get_dg_list(self):
        try:
            rdf_query = self.dg_query()
        except:
            return {}
        self.xmldg = XML(rdf_query)
        self.dglist = {}
        for dg in self.xmldg.findall("DG/DG_Info"):
            name = dg.find('name').text
            self.dglist[name] = None
        return self.dglist

    def get_dg_rdf_type(self):
        rdf_query = self.rdf_query
        self.xmldg = XML(rdf_query)
        rdftype = self.xmldg.find('DG/DG_Info/type').text
        return rdftype

    def is_rdf1_dg(self):
        if self.get_dg_rdf_type() == "RDF1":
            return True
        return False

    def is_rdf2_dg(self):
        if self.get_dg_rdf_type() == "RDF2":
            return True
        return False

    def is_rdf21_dg(self):
        if self.get_dg_rdf_type() == "RDF21":
            return True
        return False


    def get_dg_state(self):
        h = {}
        for pair in self.xmldg.findall("DG/RDF_Pair"):
            mode = pair.find('mode').text
            state = pair.find('pair_state').text
            key = mode + "/" + state
            h[key] = None
        if len(h) == 1:
            retmsg = list(h.keys())[0]
        else:
            retmsg = "mixed srdf pairs state"
        return retmsg

    def get_rdfpairs_from_dg(self):
        cmd = ['symrdf', '-g', self.symdg, '-rdfg', str(self.rdfg), 'query',
               '-output', 'xml_e']
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            raise ex.excError

        self.rdfpairs = {}   # remote_symm;remote_dev;rdfg
        self.xmldg = XML(out)

        for pair in self.xmldg.findall("DG/RDF_Pair"):
            source = pair.find('Source/dev_name').text
            target = pair.find('Target/dev_name').text
            self.rdfpairs[source] = target
        self.log.debug("rdfpairs from dg %s", str(self.rdfpairs))

    def is_synchronous_mode(self):
        cmd = ['/usr/symcli/bin/symrdf', '-g', self.symdg, '-rdfg',
               str(self.rdfg), 'verify', '-synchronous', '-i', '15', '-c', '4']
        (ret, out, err) = self.call(cmd)
        if ret == 0:
            return True
        return False

    def is_asynchronous_mode(self):
        cmd = ['/usr/symcli/bin/symrdf', '-g', self.symdg, '-rdfg',
               str(self.rdfg), 'verify', '-asynchronous', '-i', '15', '-c', '4']
        (ret, out, err) = self.call(cmd)
        if ret == 0:
            return True
        return False

    def is_acp_disk_mode(self):
        cmd = ['/usr/symcli/bin/symrdf', '-g', self.symdg, '-rdfg',
               str(self.rdfg), 'verify', '-acp_disk', '-i', '15', '-c', '4']
        (ret, out, err) = self.call(cmd)
        if ret == 0:
            return True
        return False

    def is_synchronized_state(self):
        cmd = ['/usr/symcli/bin/symrdf', '-g', self.symdg, '-rdfg',
               str(self.rdfg), 'verify', '-synchronized', '-i', '15', '-c', '4']
        (ret, out, err) = self.call(cmd)
        if ret == 0:
            return True
        return False

    def is_synchronous_and_synchronized_state(self):
        cmd = ['/usr/symcli/bin/symrdf', '-g', self.symdg, '-rdfg',
               str(self.rdfg), 'verify', '-synchronous', '-synchronized',
               '-i', '15', '-c', '4']
        (ret, out, err) = self.call(cmd)
        if ret == 0:
            return True
        return False

    def is_syncinprog_state(self):
        cmd = ['/usr/symcli/bin/symrdf', '-g', self.symdg, '-rdfg',
               str(self.rdfg), 'verify', '-syncinprog', '-i', '15', '-c', '4']
        (ret, out, err) = self.call(cmd)
        if ret == 0:
            return True
        return False

    def is_suspend_state(self):
        cmd = ['/usr/symcli/bin/symrdf', '-g', self.symdg, '-rdfg',
               str(self.rdfg), 'verify', '-suspended', '-i', '15', '-c', '4']
        (ret, out, err) = self.call(cmd)
        if ret == 0:
            return True
        return False

    def is_split_state(self):
        cmd = ['/usr/symcli/bin/symrdf', '-g', self.symdg, '-rdfg',
               str(self.rdfg), 'verify', '-split', '-i', '15', '-c', '4']
        (ret, out, err) = self.call(cmd)
        if ret == 0:
            return True
        return False

    def is_failedover_state(self):
        cmd = ['/usr/symcli/bin/symrdf', '-g', self.symdg, '-rdfg',
               str(self.rdfg), 'verify', '-failedover', '-i', '15', '-c', '4']
        (ret, out, err) = self.call(cmd)
        if ret == 0:
            return True
        return False

    def is_partitioned_state(self):
        cmd = ['/usr/symcli/bin/symrdf', '-g', self.symdg, '-rdfg',
               str(self.rdfg), 'verify', '-partitioned', '-i', '15', '-c', '4']
        (ret, out, err) = self.call(cmd)
        if ret == 0:
            return True
        return False

    # SRDF/A expected state is consistent AND enabled
    def is_consistent_state(self):
        cmd = ['/usr/symcli/bin/symrdf', '-g', self.symdg, '-rdfg',
               str(self.rdfg), 'verify', '-consistent', '-i', '15', '-c', '4']
        (ret, out, err) = self.call(cmd)
        if ret == 0:
            return True
        return False

    def is_enabled_state(self):
        cmd = ['/usr/symcli/bin/symrdf', '-g', self.symdg, '-rdfg',
               str(self.rdfg), 'verify', '-enabled', '-i', '15', '-c', '4']
        (ret, out, err) = self.call(cmd)
        if ret == 0:
            return True
        return False

    def can_sync(self, target=None):
        return True

    def resume(self):
        cmd = ['/usr/symcli/bin/symrdf', '-g', self.symdg, '-rdfg',
               str(self.rdfg), '-noprompt', 'resume', '-i', '15', '-c', '4']
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError("Failed to run command %s"% ' '.join(cmd) )
        self.flush_cache()

    def suspend(self):
        cmd = ['/usr/symcli/bin/symrdf', '-g', self.symdg, '-rdfg' ,
               str(self.rdfg), '-noprompt', 'suspend', '-i', '15', '-c', '4']
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError("Failed to run command %s"% ' '.join(cmd) )
        self.flush_cache()

    def establish(self):
        cmd = ['/usr/symcli/bin/symrdf', '-g', self.symdg, '-rdfg',
               str(self.rdfg), '-noprompt', 'establish', '-i', '15', '-c', '4']
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError("Failed to run command %s"% ' '.join(cmd) )
        self.flush_cache()

    def failover(self):
        cmd = ['/usr/symcli/bin/symrdf', '-g', self.symdg, '-rdfg',
               str(self.rdfg), '-noprompt', 'failover', '-i', '15', '-c', '4']
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError("Failed to run command %s"% ' '.join(cmd) )
        self.flush_cache()

    def failoverestablish(self):
        cmd = ['/usr/symcli/bin/symrdf', '-g', self.symdg, '-rdfg',
               str(self.rdfg), '-noprompt', 'failover', '-establish',
               '-i', '15', '-c', '4']
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError("Failed to run command %s"% ' '.join(cmd) )
        self.flush_cache()

    def split(self):
        cmd = ['/usr/symcli/bin/symrdf', '-g', self.symdg, '-rdfg',
               str(self.rdfg), '-noprompt', 'split', '-i', '15', '-c', '4']
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError
        self.flush_cache()

    def swap(self):
        cmd = ['/usr/symcli/bin/symrdf', '-g', self.symdg, '-rdfg',
               str(self.rdfg), '-noprompt', 'swap', '-i', '15', '-c', '4']
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError
        self.flush_cache()

    def get_syminfo(self):
        self.get_dg_rdf_type()

    def get_last(self):
        if self.last is not None:
            return
        for symid, symdev in self.symdev:
            ld = self.symld[symid,symdev]
            # format: Thu Feb 25 10:20:56 2010
            last = datetime.datetime.strptime(ld['clone_lastaction'], "%a %b %d %H:%M:%S %Y")
            if self.last is None or last > self.last:
                self.last = last

    def sync_status(self, verbose=False):
        try:
            self.get_syminfo()
        except ex.excError as e:
            self.status_log(str(e))
            return rcStatus.WARN
        state = self.get_dg_state()
        self.status_log("current state %s"%state, "info")
        if self.is_synchronous_and_synchronized_state():
            return rcStatus.UP
        self.status_log("expecting Synchronous/Synchronized")
        return rcStatus.WARN

    # SRDF split
    def sync_split(self):
        self.split()

    # SRDF suspend
    def sync_quiesce(self):
        self.suspend()

    # SRDF swap
    def sync_swap(self):
        self.swap()

    def sync_break(self):
        self.split()

    # SRDF establish
    def sync_resync(self):
        self.establish()

    def sync_establish(self):
        self.establish()

    def start(self):
        if rcEnv.nodename in self.svc.drpnodes:
            if self.is_rdf2_dg():
                if self.is_synchronous_and_synchronized_state():
                    self.split()
                elif self.is_partitioned_state():
                    self.log.warning("symrdf dg %s is RDF2 and partitioned. failover is preferred action."%self.symdg)
                    self.failover()
                elif self.is_failedover_state():
                    self.log.info("symrdf dg %s is already RDF2 and FailedOver."%self.symdg)
                elif self.is_suspend_state():
                    self.log.warning("symrdf dg %s is RDF2 and suspended: R2 data may be outdated"%self.symdg)
                    self.split()
                elif self.is_split_state():
                    self.log.info("symrdf dg %s is RDF2 and already splitted."%self.symdg)
                else:
                    raise ex.excError("symrdf dg %s is RDF2 on drp node and unexpected SRDF state, you have to manually return to a sane SRDF status.")
            elif self.is_rdf1_dg():
                if self.is_synchronous_and_synchronized_state():
                    pass
                else:
                    raise ex.excError("symrdf dg %s is RDF1 on drp node, you have to manually return to a sane SRDF status.")
        elif rcEnv.nodename in self.svc.nodes:
            if self.is_rdf1_dg():
                if self.is_synchronous_and_synchronized_state():
                    self.log.info("symrdf dg %s is RDF1 and synchronous/synchronized."%self.symdg)
                elif self.is_partitioned_state():
                    self.log.warning("symrdf dg %s is RDF1 and partitioned."%self.symdg)
                elif self.is_failedover_state():
                    raise ex.excError("symrdf dg %s is RDF1 and write protected, you have to manually run either sync_split+sync_establish (ie losing R2 data), or syncfailback (ie losing R1 data)"%self.symdg)
                elif self.is_suspend_state():
                    self.log.warning("symrdf dg %s is RDF1 and suspended."%self.symdg)
                elif self.is_split_state():
                    self.log.warning("symrdf dg %s is RDF1 and splitted."%self.symdg)
                else:
                    raise ex.excError("symrdf dg %s is RDF1 on primary node and unexpected SRDF state, you have to manually return to a sane SRDF status.")
            elif self.is_rdf2_dg():         # start on metrocluster passive node
                if self.is_synchronous_and_synchronized_state():
                    self.failoverestablish()
                elif self.is_partitioned_state():
                    self.log.warning("symrdf dg %s is RDF2 and partitioned, failover is preferred action."%self.symdg)
                    self.failover()
                else:
                    raise ex.excError("symrdf dg %s is RDF2 on primary node, you have to manually return to a sane SRDF status.")
        self.promote_devs_rw()

    def refresh_svcstatus(self):
        self.svcstatus = self.svc.group_status(excluded_groups=set(["sync", 'hb']))

    def get_svcstatus(self):
        if len(self.svcstatus) == 0:
            self.refresh_svcstatus()

    def __init__(self,
                 rid=None,
                 symid=None,
                 symdg=None,
                 rdfg=None,
                 symdevs=[],
                 precopy_timeout=300,
                 **kwargs):
        resSync.Sync.__init__(self,
                              rid=rid,
                              type="sync.symsrdfs",
                              **kwargs)

        self.label = "srdf/s symdg %s"%(symdg)
        self.symid = symid

        self.symdg = symdg
        self.rdfg = rdfg
        self.symdevs = symdevs
        self.precopy_timeout = precopy_timeout
        self.symdev = {}
        self.pdevs = {}
        self.svcstatus = {}
        self.symld = {}
        self.pairs = []
        self._pairs = []
        self.active_pairs = []
        self.last = None

    def __str__(self):
        return "%s symdg=%s symdevs=%s rdfg=%s" % (resSync.Sync.__str__(self),\
                self.symdg, self.symdevs, self.rdfg)

 0707010005ca02000081b40000000000000000000000015ab217540000287c000000660000000000000000000000000000002e00000000root/usr/share/opensvc/lib/resSyncSymclone.py import os
import logging

from rcGlobalEnv import rcEnv
from rcUtilities import which, justcall, lazy
import rcExceptions as ex
import rcStatus
import time
import datetime
import resSync
import xml.etree.ElementTree as ElementTree

class syncSymclone(resSync.Sync):
    def wait_for_devs_ready(self):
        pass

    def pairs_file(self, pairs=None):
        if pairs is None:
            suffix = ""
        else:
            suffix = "." + ",".join(pairs)
        return os.path.join(self.var_d, "pairs."+suffix)

    def write_pair_file(self, pairs=None):
        if pairs is None:
            _pairs = self.pairs
            key = "all"
        else:
            _pairs = pairs
            key = ",".join(pairs)
        if key in self.pairs_written:
            return
        pf = self.pairs_file(pairs)
        content = "\n".join(map(lambda x: x.replace(":", " "), _pairs))
        with open(pf, "w") as f:
            f.write(content)
        self.log.debug("wrote content '%s' in file '%s'" % (content, pf))
        self.pairs_written[key] = True

    def symclone_cmd(self, pairs=None):
        self.write_pair_file(pairs)
        return ['/usr/symcli/bin/symclone', '-sid', self.symid, '-f', self.pairs_file(pairs)]

    @lazy
    def active_pairs(self):
        active_pairs = []
        etree = self._showdevs()
        for e in etree.findall("Symmetrix/Device"):
            clone = e.find("CLONE_Device")
            if clone is None:
                dev = e.find("Dev_Info/dev_name").text
                self.status_log("%s has no clone info" % dev)
                continue
            src = e.find("CLONE_Device/SRC/dev_name").text
            dst = e.find("CLONE_Device/TGT/dev_name").text
            state = e.find("CLONE_Device/state").text.lower()
            if state in self.active_states:
                active_pairs.append(src+":"+dst)
        return active_pairs

    def is_active(self):
        self.unset_lazy("active_pairs")
        if len(self.active_pairs) == len(self.pairs):
            return True
        return False

    def is_activable(self):
        for state in self.activable_states:
            cmd = self.symclone_cmd() + ['verify', '-'+state]
            (ret, out, err) = self.call(cmd)
            if ret == 0:
                return True
        return False

    def wait_restored(self):
        interval = 10
        count = self.restore_timeout // interval
        if count == 0:
            count = 1
        cmd = self.symclone_cmd() + ['verify', '-restored', '-i', str(interval), '-c', str(count)]
        ret, out, err = self.vcall(cmd)
        if ret == 0:
           return
        cmd = self.symclone_cmd() + ['-noprompt', 'terminate', '-restored']
        cmd = " ".join(cmd)
        raise ex.excError("timeout waiting for devs to become 'restored'. "
                          "once restored, you can terminate running: " + cmd)

    def wait_for_active(self):
        delay = 10
        ass = " or ".join(self.active_states)
        for i in range(self.activate_timeout//delay+1):
            if self.is_active():
                return
            if i == 0:
                self.log.info("waiting for active state (max %i secs, %s)" % (self.activate_timeout, ass))
            time.sleep(delay)
        self.log.error("timed out waiting for active state (%i secs, %s)" % (self.activate_timeout, ass))
        ina = set(self.pairs) - set(self.active_pairs)
        ina = ", ".join(ina)
        raise ex.excError("%s still not in active state (%s)" % (ina, ass))

    def wait_for_activable(self):
        delay = 10
        ass = " or ".join(self.activable_states)
        for i in range(self.recreate_timeout//delay+1):
            if self.is_activable():
                return
            if i == 0:
                self.log.info("waiting for activable state (max %i secs, %s)" % (self.recreate_timeout, ass))
            time.sleep(delay)
        raise ex.excError("timed out waiting for activable state (%i secs, %s)" % (self.recreate_timeout, ass))

    def activate(self):
        if self.is_active():
            self.log.info("symclone target devices are already active")
            return
        self.wait_for_activable()
        self.unset_lazy("last")
        cmd = self.symclone_cmd() + ['-noprompt', 'activate', '-i', '20', '-c', '30']
        if self.consistent:
            cmd.append("-consistent")
        (ret, out, err) = self.vcall(cmd, warn_to_info=True)
        if ret != 0:
            raise ex.excError
        self.wait_for_active()
        self.wait_for_devs_ready()

    def can_sync(self, target=None):
        try:
            self.check_requires("sync_update")
        except ex.excError as e:
            self.log.debug(e)
            return False

        if self.skip_sync(self.last):
            return False
        return True

    def recreate(self):
        if self.skip_sync(self.last):
            return
        if self.is_activable():
            self.log.info("symclone are already recreated")
            return
        cmd = self.symclone_cmd() + ['-noprompt', 'recreate', '-i', '20', '-c', '30']
        if self.type == "sync.symclone" and self.precopy:
            cmd.append("-precopy")
        (ret, out, err) = self.vcall(cmd, warn_to_info=True)
        if ret != 0:
            raise ex.excError

    def restore(self):
        self.unset_lazy("last")
        cmd = self.symclone_cmd() + ['-noprompt', 'restore']
        (ret, out, err) = self.vcall(cmd, warn_to_info=True)
        if ret != 0:
            raise ex.excError(err)

    def terminate_restore(self):
        self.unset_lazy("last")
        cmd = self.symclone_cmd() + ['-noprompt', 'terminate', '-restored']
        (ret, out, err) = self.vcall(cmd, warn_to_info=True)
        if ret != 0:
            raise ex.excError(err)

    def _info(self):
        data = [
          ["precopy", str(self.precopy)],
          ["pairs", str(self.pairs)],
          ["symid", str(self.symid)],
          ["consistent", str(self.consistent)],
        ]
        return data

    def split_pair(self, pair):
        l = pair.split(":")
        if len(l) != 2:
            raise ex.excError("pair %s malformed" % pair)
        return l

    def _showdevs(self):
        dst_devs = map(lambda x: x.split(":")[1], self.pairs)
        cmd = ['/usr/symcli/bin/symdev', '-sid', self.symid, 'list', '-v', '-devs', ','.join(dst_devs), '-output', 'xml_e']
        out, err, ret = justcall(cmd)
        etree = ElementTree.fromstring(out)
        return etree

    @lazy
    def showdevs_etree(self):
        data = {}
        etree = self._showdevs()
        for e in etree.findall("Symmetrix/Device"):
            dev_name = e.find("Dev_Info/dev_name").text
            data[dev_name] = e
        return data

    def last_action_dev(self, dev):
        # format: Thu Feb 25 10:20:56 2010
        try:
            s = self.showdevs_etree[dev].find("CLONE_Device/last_action").text
            return datetime.datetime.strptime(s, "%a %b %d %H:%M:%S %Y")
        except AttributeError:
            return

    @lazy
    def last(self):
        self.unset_lazy("showdevs_etree")
        _last = None
        for pair in self.pairs:
            src, dst = self.split_pair(pair)
            dev_last = self.last_action_dev(dst)
            if dev_last is None:
                continue
            if _last is None or dev_last > _last:
                _last = dev_last
        return _last

    def _status(self, verbose=False):
        if self.last is None:
            return rcStatus.DOWN
        if len(self.active_pairs) not in (len(self.pairs), 0):
            self.status_log("cloneset has %d/%d active devs" % (len(self.active_pairs), len(self.pairs)))
            return rcStatus.WARN
        elif self.last < datetime.datetime.now() - datetime.timedelta(minutes=self.sync_max_delay):
            self.status_log("Last sync on %s older than %d minutes"%(self.last, self.sync_max_delay))
            return rcStatus.WARN
        else:
            self.status_log("Last sync on %s" % self.last, "info")
            return rcStatus.UP

    def sync_break(self):
        self.activate()

    def sync_resync(self):
        self.recreate()

    def sync_update(self):
        self.recreate()
        self.activate()

    def sync_restore(self):
        if not self.is_active():
            ina = set(self.pairs) - set(self.active_pairs)
            ina = ", ".join(ina)
            ass = " or ".join(self.active_states)
            raise ex.excError("%s not in active state (%s)" % (ina, ass))
        self.restore()
        self.wait_restored()
        self.terminate_restore()

    def start(self):
        self.activate()

    def __init__(self,
                 rid=None,
                 type="sync.symclone",
                 symid=None,
                 pairs=[],
                 precopy=True,
                 consistent=True,
                 restore_timeout=None,
                 recreate_timeout=None,
                 **kwargs):
        resSync.Sync.__init__(self,
                              rid=rid,
                              type=type,
                              **kwargs)

        if self.type == "sync.symclone":
            self.active_states = ["copied", "copyinprog"]
            self.activable_states = ["recreated", "precopy"]
        elif self.type == "sync.symsnap":
            self.active_states = ["copyonwrite"]
            self.activable_states = ["recreated", "created"]
        else:
            raise ex.excInitError("unsupported symclone driver type %s", self.type)
        self.activate_timeout = 20
        self.recreate_timeout = recreate_timeout
        self.restore_timeout = restore_timeout
        self.precopy = precopy
        self.pairs_written = {}
        self.label = "symclone symid %s pairs %s" % (symid, " ".join(pairs))
        if len(self.label) > 80:
            self.label = self.label[:76] + "..."
        self.symid = symid
        self.pairs = pairs
        self.consistent = consistent
        self.svcstatus = {}
        self.default_schedule = "@0"
        if restore_timeout is None:
            self.restore_timeout = 300
        else:
            self.restore_timeout = restore_timeout


    def __str__(self):
        return "%s symid=%s pairs=%s" % (resSync.Sync.__str__(self),\
                self.symid, str(self.pairs))

0707010005ca03000081b40000000000000000000000015ab21754000009a9000000660000000000000000000000000000003300000000root/usr/share/opensvc/lib/resSyncSymcloneLinux.py    import os
import logging

from rcGlobalEnv import rcEnv
import rcExceptions as ex
import rcStatus
import resources as Res
import time
import datetime
import resSyncSymclone as symclone
from rcUtilities import which

class syncSymclone(symclone.syncSymclone):
    def dev_rescan(self, dev):
        dev = dev.replace('/dev/', '')
        sysdev = "/sys/block/%s/device/rescan"%dev
        self.log.info("echo 1>%s"%sysdev)
        with open(sysdev, 'w') as s:
            s.write("1")

    def refresh_multipath(self, dev):
        if which(rcEnv.syspaths.multipath) is None:
            return
        cmd = [rcEnv.syspaths.multipath, '-v0', '-r', dev]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def dev_ready(self, dev):
        cmd = ['sg_turs', dev]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            return False
        return True

    def wait_for_dev_ready(self, dev):
        delay = 1
        timeout = 5
        for i in range(timeout/delay):
            if self.dev_ready(dev):
                return
            if i == 0:
                self.log.info("waiting for device %s to become ready (max %i secs)"%(dev,timeout))
            time.sleep(delay)
        self.log.error("timed out waiting for device %s to become ready (max %i secs)"%(dev,timeout))
        raise ex.excError

    def wait_for_devs_ready(self):
        for pair in self.pairs:
            src, dst = self.split_pair(pair)
            dev = self.showdevs_etree[dst].find('Dev_Info/pd_name').text
            if "Not Visible" in dev:
                raise ex.excError("pd name is 'Not Visible'. please scan scsi buses and run symcfg discover")
            self.dev_rescan(dev)
            self.wait_for_dev_ready(dev)
            self.refresh_multipath(dev)

    def __init__(self,
                 rid=None,
                 type="sync.symclone",
                 symid=None,
                 pairs=[],
                 precopy=True,
                 consistent=True,
                 **kwargs):
        symclone.syncSymclone.__init__(self,
                                       rid=rid,
                                       type=type,
                                       symid=symid,
                                       pairs=pairs,
                                       precopy=precopy,
                                       consistent=consistent,
                                       **kwargs)

   0707010005ca04000081b40000000000000000000000015ab2175400003af0000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/resSyncZfs.py  import os
import datetime
from subprocess import *

from rcGlobalEnv import rcEnv
from rcUtilities import justcall
import rcExceptions as ex
import rcStatus
import resSync
from rcZfs import a2pool_dataset, Dataset
from rcUtilities import bdecode

class SyncZfs(resSync.Sync):
    """define zfs sync resource to be zfs send/zfs receive between nodes
    """
    def __init__(self,
                 rid=None,
                 target=None,
                 src=None,
                 dst=None,
                 recursive = True,
                 snap_size=0,
                 **kwargs):
        resSync.Sync.__init__(self,
                              rid=rid,
                              type="sync.zfs",
                              **kwargs)

        self.label = "zfs of %s to %s"%(src, ",".join(target))
        self.target = target
        self.recursive = recursive
        self.src = src
        self.dst = dst
        (self.src_pool, self.src_ds) = a2pool_dataset(src)
        (self.dst_pool, self.dst_ds) = a2pool_dataset(dst)

    def _info(self):
        data = [
          ["src", self.src],
          ["dst", self.dst],
          ["target", " ".join(self.target) if self.target else ""],
          ["recursive", str(self.recursive).lower()],
        ]
        data += self.stats_keys()
        return data

    def pre_action(self, action):
        """Prepare dataset snapshots
        Don't sync PRD services when running on !PRD node
        skip snapshot creation if delay_snap in tags
        delay_snap should be used for oracle archive datasets
        """
        if not hasattr(self, action):
            return
        resources = [r for r in self.rset.resources if \
                     not r.skip and not r.is_disabled() and \
                     r.type == self.type]

        if len(resources) == 0:
            return

        self.pre_sync_check_prd_svc_on_non_prd_node()
        self.pre_sync_check_svc_not_up()
        self.pre_sync_check_flex_primary()

        for i, r in enumerate(resources):
            if 'delay_snap' in r.tags:
                continue
            r.get_info()
            if action in ['sync_update', 'sync_resync', 'sync_drp', 'sync_nodes']:
                if action == 'sync_nodes' and self.target != ['nodes']:
                    return
                if action == 'sync_drp' and self.target != ['drpnodes']:
                    return
                nb = 0
                tgts = r.targets.copy()
                if len(tgts) == 0 :
                    continue
            if not r.snap_exists(r.src_snap_tosend):
                r.create_snap(r.src_snap_tosend)

    def __str__(self):
        return "%s target=%s src=%s" % (resSync.Sync.__str__(self),\
                self.target, self.src)

    def snap_exists(self, snapname, node=None):
        cmd = [rcEnv.syspaths.zfs, 'list', '-t', 'snapshot', snapname]
        if node is not None:
            cmd = rcEnv.rsh.split() + [node] + cmd
        out, err, ret = justcall(cmd)
        if ret == 0:
            return True
        else:
            return False

    def create_snap(self, snap):
        snapds = Dataset(snap)
        if snapds.exists():
            self.log.error('%s should not exist'%snap)
            raise ex.excError
        if self.recursive :
            cmd = [rcEnv.syspaths.zfs, 'snapshot' , '-r' , snap]
        else:
            cmd = [rcEnv.syspaths.zfs, 'snapshot' , snap]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def get_src_info(self):
        self.src_snap_sent_old = self.src_ds + "@sent"
        self.src_snap_tosend_old = self.src_ds + "@tosend"
        self.src_snap_sent = self.src_ds + "@"+self.rid.replace("#",".") + ".sent"
        self.src_snap_tosend = self.src_ds + "@"+self.rid.replace("#",".") + ".tosend"

    def get_dst_info(self):
        self.dst_snap_sent_old = self.dst_ds + "@sent"
        self.dst_snap_tosend_old = self.dst_ds + "@tosend"
        self.dst_snap_sent = self.dst_ds + "@"+self.rid.replace("#",".") + ".sent"
        self.dst_snap_tosend = self.dst_ds + "@"+self.rid.replace("#",".") + ".tosend"

    def get_peersenders(self):
        self.peersenders = set()
        if rcEnv.nodename not in self.svc.nodes or self.target != ["drpnodes"]:
            return
        self.peersenders |= self.svc.nodes
        self.peersenders -= set([rcEnv.nodename])

    def get_targets(self):
        self.targets = set()
        if 'nodes' in self.target:
            self.targets |= self.svc.nodes
        if 'drpnodes' in self.target:
            self.targets |= self.svc.drpnodes
        self.targets -= set([rcEnv.nodename])

    def get_info(self):
        self.get_targets()
        self.get_src_info()
        self.get_dst_info()

    def sync_nodes(self):
        """
        Run sync_update if the target contains only nodes.
        """
        if self.target == ["nodes"]:
            self.sync_update()
        else:
            self.log.warning("skip: target also has drp nodes.")

    def sync_drp(self):
        """
        Run sync_update if the target contains only drpnodes.
        """
        if self.target == ["drpnodes"]:
            self.sync_update()
        else:
            self.log.warning("skip: target is not only drp nodes.")

    def sync_full(self):
        """
        Purge all local and remote snaps, and call sync_update, which
        will do a non-incremental send/recv.
        """
        self.destroy_all_snaps()
        self.sync_update()

    def destroy_all_snaps(self):
        self.force_remove_snap(self.src_snap_tosend)
        self.force_remove_snap(self.src_snap_sent)
        for node in self.targets:
            self.force_remove_snap(self.dst_snap_tosend, node)
            self.force_remove_snap(self.dst_snap_sent, node)

    def do_it(self, send_cmd, receive_cmd, node):
        self.log.info(' '.join(send_cmd + ["|"] + receive_cmd))
        p1 = Popen(send_cmd, stdout=PIPE)
        pi = Popen(["dd", "bs=4096"], stdin=p1.stdout, stdout=PIPE, stderr=PIPE)
        p2 = Popen(receive_cmd, stdin=pi.stdout, stdout=PIPE, stderr=PIPE)
        buff =  p2.communicate()
        if p2.returncode == 0:
            stats_buff = pi.communicate()[1]
            stats = self.parse_dd(stats_buff)
            self.update_stats(stats, target=node)
        out = bdecode(buff[0])
        err = bdecode(buff[1])
        if p2.returncode != 0:
            if err is not None and len(err) > 0:
                self.log.error(err)
            raise ex.excError("sync failed")
        if out is not None and len(out) > 0:
            self.log.info(out)

    def zfs_send_incremental(self, node):
        if not self.snap_exists(self.src_snap_sent, node):
            return self.zfs_send_initial(node)
        if self.recursive:
            send_cmd = [rcEnv.syspaths.zfs, "send", "-R", "-I",
                        self.src_snap_sent, self.src_snap_tosend]
        else:
            send_cmd = [rcEnv.syspaths.zfs, "send", "-I",
                        self.src_snap_sent, self.src_snap_tosend]

        receive_cmd = [rcEnv.syspaths.zfs, "receive", "-dF", self.dst_pool]
        if node is not None:
            _receive_cmd = rcEnv.rsh.strip(' -n').split()
            if "-q" in _receive_cmd:
                _receive_cmd.remove("-q")
            receive_cmd = _receive_cmd + [node] + receive_cmd

        self.do_it(send_cmd, receive_cmd, node)

    def zfs_send_initial(self, node=None):
        if self.recursive:
            send_cmd = [rcEnv.syspaths.zfs, "send", "-R",
                        self.src_snap_tosend]
        else:
            send_cmd = [rcEnv.syspaths.zfs, "send", "-p",
                        self.src_snap_tosend]

        receive_cmd = [rcEnv.syspaths.zfs, "receive", "-dF", self.dst_pool]
        if node is not None:
            _receive_cmd = rcEnv.rsh.strip(' -n').split()
            if "-q" in _receive_cmd:
                _receive_cmd.remove("-q")
            receive_cmd = _receive_cmd + [node] + receive_cmd

        self.do_it(send_cmd, receive_cmd, node)

    def force_remove_snap(self, snap, node=None):
        try:
            self.remove_snap(snap, node=node, check_exists=False)
        except ex.excError:
            pass

    def remove_snap(self, snap, node=None, check_exists=True):
        if check_exists and not self.snap_exists(snap, node=node):
            return
        if self.recursive :
            cmd = [rcEnv.syspaths.zfs, 'destroy', '-r', snap]
        else:
            cmd = [rcEnv.syspaths.zfs, 'destroy', snap]
        if node is not None:
            cmd = rcEnv.rsh.split() + [node] + cmd
        if check_exists:
            err_to_info = False
        else:
            err_to_info = True
        (ret, out, err) = self.vcall(cmd, err_to_info=err_to_info)
        if ret != 0:
            raise ex.excError

    def rename_snap(self, src, dst,  node=None):
        if self.snap_exists(dst, node):
            self.log.error("%s should not exist"%dst)
            raise ex.excError
        if self.recursive :
            cmd = [rcEnv.syspaths.zfs, 'rename', '-r', src, dst]
        else:
            cmd = [rcEnv.syspaths.zfs, 'rename', src, dst]

        if node is not None:
            cmd = rcEnv.rsh.split() + [node] + cmd
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def rotate_snaps(self, src, dst, node=None):
        self.remove_snap(dst, node)
        self.rename_snap(src, dst, node)

    def sync_update(self):
        """
        test if service status is UP else return
        create the snap_tosend if not already created (during pre_action)
        if a snap has already been sent
        then for all targets
             zfs_send_incremental
             rotate snap
        else for all targets
            zfs_send_initial
            rotate snap
        rotate snap on local node
        """
        self.pre_sync_check_svc_not_up()
        self.pre_sync_check_flex_primary()

        self.get_info()

        if self.snap_exists(self.src_snap_sent_old):
            self.log.info("migrate local sent snap name")
            self.rename_snap(self.src_snap_sent_old, self.src_snap_sent)
            for node in self.targets:
                self.log.info("migrate sent snap name on node %s", node)
                self.rename_snap(self.dst_snap_sent_old, self.dst_snap_sent, node=node)

        if not self.snap_exists(self.src_snap_tosend):
            self.create_snap(self.src_snap_tosend)

        if self.snap_exists(self.src_snap_sent):
            for n in self.targets:
                self.remove_snap(self.dst_snap_tosend, n)
                self.zfs_send_incremental(n)
                self.rotate_snaps(self.dst_snap_tosend, self.dst_snap_sent, n)
        else:
            for n in self.targets:
                self.remove_snap(self.dst_snap_tosend, n)
                self.zfs_send_initial(n)
                self.rotate_snaps(self.dst_snap_tosend, self.dst_snap_sent, n)
        self.rotate_snaps(self.src_snap_tosend, self.src_snap_sent)
        self.write_statefile()
        for n in self.targets:
            self.push_statefile(n)
        self.write_stats()

    def can_sync(self, target=None):
        if not Dataset(self.src).exists():
            return False
        try:
            ls = self.get_local_state()
            ts = datetime.datetime.strptime(ls['date'], "%Y-%m-%d %H:%M:%S.%f")
        except IOError:
            self.log.debug("zfs state file not found")
            return True
        except:
            import sys
            import traceback
            e = sys.exc_info()
            print(e[0], e[1], traceback.print_tb(e[2]))
            return False
        if self.skip_sync(ts):
            self.status_log("Last sync on %s older than %i minutes"%(ts, self.sync_max_delay))
            return False
        return True

    def sync_status(self, verbose=False):
        try:
            ls = self.get_local_state()
            now = datetime.datetime.now()
            last = datetime.datetime.strptime(ls['date'], "%Y-%m-%d %H:%M:%S.%f")
            delay = datetime.timedelta(minutes=self.sync_max_delay)
        except IOError:
            self.status_log("zfs state file not found")
            return rcStatus.WARN
        except:
            import sys
            import traceback
            e = sys.exc_info()
            print(e[0], e[1], traceback.print_tb(e[2]))
            return rcStatus.WARN
        if last < now - delay:
            self.status_log("Last sync on %s older than %i minutes"%(last, self.sync_max_delay))
            return rcStatus.WARN
        return rcStatus.UP

    def check_remote(self, node):
        rs = self.get_remote_state(node)
        if self.snap1_uuid != rs['uuid']:
            self.log.error("%s last update uuid doesn't match snap1 uuid"%(node))
            raise ex.excError

    def get_remote_state(self, node):
        self.set_statefile()
        cmd1 = ['env', 'LANG=C', 'cat', self.statefile]
        cmd = rcEnv.rsh.split() + [node] + cmd1
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            self.log.error("could not fetch %s last update uuid"%node)
            raise ex.excError
        return self.parse_statefile(out, node=node)

    def get_local_state(self):
        self.set_statefile()
        with open(self.statefile, 'r') as f:
            out = f.read()
        return self.parse_statefile(out)

    def get_snap_uuid(self, snap):
        cmd = [rcEnv.syspaths.zfs, 'list', '-H', '-o', 'creation', '-t', 'snapshot', snap]
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            raise ex.excError
        self.snap_uuid = out.strip()

    def set_statefile(self):
        self.statefile = os.path.join(self.var_d, 'zfs_state')

    def write_statefile(self):
        self.set_statefile()
        self.get_snap_uuid(self.src_snap_sent)
        self.log.info("update state file with snap uuid %s"%self.snap_uuid)
        with open(self.statefile, 'w') as f:
             f.write(str(datetime.datetime.now())+';'+self.snap_uuid+'\n')

    def _push_statefile(self, node):
        cmd = rcEnv.rcp.split() + [self.statefile, node+':'+self.statefile.replace('#', '\#')]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

    def push_statefile(self, node):
        self.set_statefile()
        self._push_statefile(node)
        self.get_peersenders()
        for node in self.peersenders:
            self._push_statefile(node)

    def parse_statefile(self, out, node=None):
        self.set_statefile()
        if node is None:
            node = rcEnv.nodename
        lines = out.strip().split('\n')
        if len(lines) != 1:
            self.log.error("%s:%s is corrupted"%(node, self.statefile))
            raise ex.excError
        fields = lines[0].split(';')
        if len(fields) != 2:
            self.log.error("%s:%s is corrupted"%(node, self.statefile))
            raise ex.excError
        return dict(date=fields[0], uuid=fields[1])

0707010005ca05000081b40000000000000000000000015ab2175400001620000000660000000000000000000000000000002d00000000root/usr/share/opensvc/lib/resSyncZfsSnap.py  import os

from rcGlobalEnv import rcEnv
import rcExceptions as ex
import rcStatus
import time
import datetime
import resSync
import rcZfs
from rcUtilities import justcall

class syncZfsSnap(resSync.Sync):
    def __init__(self,
                 rid=None,
                 name=None,
                 dataset=[],
                 keep=1,
                 recursive=True,
                 **kwargs):
        resSync.Sync.__init__(self,
                              rid=rid, type="sync.zfssnap",
                              **kwargs)

        if name:
            self.label = "zfs '%s' snapshot %s" % (name, ", ".join(dataset))
        else:
            self.label = "zfs snapshot %s" % ", ".join(dataset)
        self.dataset = dataset
        self.recursive = recursive
        self.keep = keep
        self.name = name
        self.zfs = {}

    def _info(self):
        data = [
          ["dataset", " ".join(self.dataset)],
          ["name", self.name if self.name else ""],
          ["keep", str(self.keep)],
          ["recursive", str(self.recursive).lower()],
          ["sync_max_delay", str(self.sync_max_delay) if self.sync_max_delay else ""],
          ["schedule", self.schedule if self.schedule else ""],
        ]
        return data

    def on_add(self):
        pass

    def create_snap(self, dataset):
        ds = rcZfs.Dataset(dataset, log=self.log)
        snap = ""
        if self.name:
            suffix = self.name
        else:
            suffix = ""
        suffix += ".snap.%Y-%m-%d.%H:%M:%S"
        snap += datetime.datetime.now().strftime(suffix)
        try:
            ds.snapshot(snapname=snap, recursive=self.recursive)
        except Exception as e:
            raise ex.excError(str(e))

    def list_snaps(self, dataset):
        cmd = ["zfs", "list", "-r", "-H", "-t", "snapshot", "-o", "name"]
        out, err, ret = justcall(cmd)
        return out.splitlines()

    def remove_snap(self, dataset):
        cursnaps = self.list_snaps(dataset)
        snaps = {}
        for sv in cursnaps:
            s = sv.replace(dataset+"@", "")
            l = s.split('.')
            if len(l) < 2:
                continue
            if l[0] != self.name or l[1] != "snap":
                continue
            try:
                ds = sv.split(".snap.")[-1]
                d = datetime.datetime.strptime(ds, "%Y-%m-%d.%H:%M:%S")
                snaps[ds] = sv
            except Exception as e:
                pass
        if len(snaps) <= self.keep:
            return
        sorted_snaps = []
        for ds in sorted(snaps.keys(), reverse=True):
            sorted_snaps.append(snaps[ds])
        for path in sorted_snaps[self.keep:]:
            try:
                ds = rcZfs.Dataset(path, log=self.log)
                if self.recursive:
                    options = ["-r"]
                else:
                    options = []
                ds.destroy(options=options)
            except Exception as e:
                raise ex.excError(str(e))

    def _status_one(self, dataset):
        try:
            ds = rcZfs.Dataset(dataset, log=self.log)
        except Exception as e:
            self.status_log("%s %s" % (dataset, str(e)))
            return
        snaps = []
        for sv in self.list_snaps(dataset):
            s = sv.replace(dataset+"@", "")
            l = s.split('.')
            if len(l) < 2:
                continue
            if l[0] != self.name or l[1] != "snap":
                continue
            try:
                ds = sv.split(".snap.")[-1]
                d = datetime.datetime.strptime(ds, "%Y-%m-%d.%H:%M:%S")
                snaps.append(d)
            except Exception as e:
                pass
        if len(snaps) == 0:
            self.status_log("%s has no snap" % dataset)
            return
        if len(snaps) > self.keep:
            self.status_log("%s has %d too many snaps" % (dataset, len(snaps)-self.keep))
        last = sorted(snaps, reverse=True)[0]
        limit = datetime.datetime.now() - datetime.timedelta(minutes=self.sync_max_delay)
        if last < limit:
            self.status_log("%s last snap is too old (%s)" % (dataset, last.strftime("%Y-%m-%d %H:%M:%S")))

    def sync_status(self, verbose=False):
        self.remove_snaps()
        for dataset in self.dataset:
            if dataset.count("/") < 1:
                self.status_log("misformatted dataset entry %s (expected <pool>/<ds>)" % dataset)
                continue
            self._status_one(dataset)
        issues = set(self.status_logs_get(["warn"])) - set([''])
        if len(issues) == 0:
            return rcStatus.UP
        return rcStatus.WARN

    def can_update(self):
        s = self.svc.group_status(excluded_groups=set(["sync", "hb", "app"]))
        if not self.svc.options.force and \
           s['avail'].status not in [rcStatus.UP, rcStatus.NA]:
            return False
        return True

    def _sync_update(self, dataset):
        if self.can_update():
            self.create_snap(dataset)
        elif not self.svc.options.cron:
            self.log.info("skip snapshot creation on instance not up")
        self.remove_snap(dataset)

    def sync_update(self):
        pass

    def pre_action(self, action):
        """
        Do the snaps in pre_action so the zfs send/recv can replicate them asap
        """
        for dataset in self.dataset:
            self._sync_update(dataset)

    def remove_snaps(self):
        for dataset in self.dataset:
            self.remove_snap(dataset)

    def __str__(self):
        return "%s dataset=%s keep=%s" % (resSync.Sync.__str__(self), str(self.dataset), str(self.keep))

0707010005ca06000081b40000000000000000000000015ab2175400000e6c000000660000000000000000000000000000002600000000root/usr/share/opensvc/lib/resTask.py import os
import pwd
import sys
import resources as Res
import lock
import rcStatus
import rcExceptions as ex
from rcGlobalEnv import rcEnv

if sys.version_info[0] >= 3:
    raw_input = input

def run_as_popen_kwargs(user):
    if rcEnv.sysname == "Windows":
        return {}
    if user is None:
        return {}
    cwd = rcEnv.paths.pathtmp
    import pwd
    try:
        pw_record = pwd.getpwnam(user)
    except Exception as exc:
        raise ex.excError("user lookup failure: %s" % str(exc))
    user_name      = pw_record.pw_name
    user_home_dir  = pw_record.pw_dir
    user_uid  = pw_record.pw_uid
    user_gid  = pw_record.pw_gid
    env = os.environ.copy()
    env['HOME']  = user_home_dir
    env['LOGNAME']  = user_name
    env['PWD']  = cwd
    env['USER']  = user_name
    return {'preexec_fn': demote(user_uid, user_gid), 'cwd': cwd, 'env': env}

def demote(user_uid, user_gid):
    def result():
        os.setgid(user_gid)
        os.setuid(user_uid)
    return result

class Task(Res.Resource):
    default_optional = True
    def __init__(self,
                 rid=None,
                 command=None,
                 user=None,
                 on_error=None,
                 confirmation=False,
                 **kwargs):
        Res.Resource.__init__(self, rid, type="task", **kwargs)
        self.command = command
        self.on_error = on_error
        self.user = user
        self.confirmation = confirmation

    def __str__(self):
        return "%s command=%s user=%s" % (Res.Resource.__str__(self), self.command, str(self.user))

    def _info(self):
        data = [
          ["command", self.command],
        ]
        if self.on_error:
            data.append(["on_error", self.on_error])
        if self.user:
            data.append(["user", self.user])
        return data

    def has_it(self):
        return False

    def is_up(self):
        return False

    def stop(self):
        pass

    def start(self):
        pass

    @staticmethod
    def alarm_handler(signum, frame):
        raise ex.excSignal

    def confirm(self):
        """
        Ask for an interactive confirmation. Raise if the user aborts or
        if no input is given before timeout.
        """
        if not self.confirmation:
            return
        import signal
        signal.signal(signal.SIGALRM, self.alarm_handler)
        signal.alarm(30)

        print("This task run requires confirmation.\nPlease make sure you fully "
              "understand its role and effects before confirming the run.")
        try:
            buff = raw_input("Do you really want to run %s (yes/no) > " % self.rid)
        except ex.excSignal:
            raise ex.excError("timeout waiting for confirmation")

        if buff == "yes":
            signal.alarm(0)
            self.log.info("run confirmed")
        else:
            raise ex.excError("run aborted")

    def run(self):
        try:
            with lock.cmlock(lockfile=os.path.join(self.var_d, "run.lock"), timeout=0):
                self._run()
        except Exception:
            raise ex.excError("task is already running (maybe too long for the schedule)")

    def _run(self):
        kwargs = {
          'blocking': True,
        }
        kwargs.update(run_as_popen_kwargs(self.user))

        try:
            self.action_triggers("", "command", **kwargs)
        except ex.excError:
            if self.on_error:
                kwargs["blocking"] = False
                self.action_triggers("", "on_error", **kwargs)
            raise ex.excError

    def _status(self, verbose=False):
        return rcStatus.NA

    def is_provisioned(self, refresh=False):
        return True

0707010005ca07000081b40000000000000000000000015ab2175400008b99000000660000000000000000000000000000002800000000root/usr/share/opensvc/lib/resources.py   """
Defines the resource class, which is the parent class of every
resource driver.
"""
from __future__ import print_function

import os
import logging
import sys
import time
import json

import lock
import rcExceptions as ex
import rcStatus
from rcUtilities import lazy, clear_cache, call, vcall, lcall, set_lazy, \
                        action_triggers, mimport, unset_lazy
from rcGlobalEnv import rcEnv
import rcColor

ALLOW_ACTION_WITH_NOACTION = [
    "presync",
]

class Resource(object):
    """
    Resource drivers parent class
    """
    default_optional = False
    def __init__(self,
                 rid=None,
                 type=None,
                 subset=None,
                 optional=False,
                 disabled=False,
                 monitor=False,
                 restart=0,
                 tags=None,
                 standby=False,
                 skip_provision=False,
                 skip_unprovision=False,
                 shared=False,
                 promote_rw=False,
                 encap=False):
        if tags is None:
            tags = set()
        self.svc = None
        self.rset = None
        self.rid = rid
        self.tags = tags
        self.type = type
        self.subset = subset
        self.optional = optional if optional is not None else self.default_optional
        self.standby = standby
        self.disabled = disabled
        self.skip = False
        self.monitor = monitor
        self.nb_restart = restart
        self.rstatus = None
        self.skip_provision = skip_provision
        self.skip_unprovision = skip_unprovision
        self.shared = shared
        self.need_promote_rw = promote_rw
        self.encap = encap or "encap" in self.tags
        self.sort_key = rid
        self.info_in_status = []
        self.lockfd = None
        try:
            self.label = type
        except AttributeError:
            # label is a lazy prop of the child class
            pass
        self.status_logs = []
        self.can_rollback = False

    @lazy
    def log(self):
        """
        Lazy init for the resource logger.
        """
        return logging.getLogger(self.log_label())

    @lazy
    def var_d(self):
        var_d = os.path.join(self.svc.var_d, self.rid)
        if not os.path.exists(var_d):
            os.makedirs(var_d)
        return var_d

    def set_logger(self, log):
        """
        Set the <log> logger as the resource logger, in place of the default
        lazy-initialized one.
        """
        set_lazy(self, "log", log)

    def fmt_info(self, keys=None):
        """
        Returns the resource generic keys sent to the collector upon push
        resinfo.
        """
        if keys is None:
            return []
        for idx, key in enumerate(keys):
            if len(key) == 2:
                keys[idx] = [
                    self.svc.svcname,
                    self.svc.node.nodename,
                    self.svc.topology,
                    self.rid
                ] + key
            elif len(key) == 3:
                keys[idx] = [
                    self.svc.svcname,
                    self.svc.node.nodename,
                    self.svc.topology
                ] + key
        return keys

    def log_label(self):
        """
        Return the resource label used in logs entries.
        """
        label = rcEnv.nodename + "."
        if hasattr(self, "svc"):
            label += self.svc.svcname + '.'

        if self.rid is None:
            label += self.type
            return label

        if self.subset is None:
            label += self.rid
            return label

        elements = self.rid.split('#')
        if len(elements) != 2:
            label += self.rid
            return label

        ridx = elements[1]
        label += "%s:%s#%s" % (self.type.split(".")[0], self.subset, ridx)
        return label

    def __str__(self):
        output = "object=%s rid=%s type=%s" % (
            self.__class__.__name__,
            self.rid,
            self.type
        )
        if self.optional:
            output += " opt=" + str(self.optional)
        if self.disabled:
            output += " disa=" + str(self.disabled)
        return output

    def __lt__(self, other):
        """
        Resources needing to be started or stopped in a specific order
        should redefine that.
        """
        if self.rid+"pr" == other.rid:
            return False
        if self.rid == other.rid+"pr":
            return True
        return self.sort_key < other.sort_key

    def save_exc(self):
        """
        A helper method to save stacks in the service log.
        """
        self.log.error("unexpected error. stack saved in the service debug log")
        self.log.debug("", exc_info=True)

    def setup_environ(self):
        """
        Setup environement variables for use by triggers and startup
        scripts. This method needs defining in each class with their
        class variable.
        Env vars names should, by convention, be prefixed by OPENSVC_
        """
        pass

    def is_optional(self):
        """
        Accessor for the optional resource property.
        """
        return self.optional

    def is_disabled(self):
        """
        Accessor for the disabled resource property.
        """
        if self.svc.disabled:
            return True
        return self.disabled

    def set_optional(self):
        """
        Set the optional resource property to True.
        """
        self.optional = True

    def unset_optional(self):
        """
        Set the optional resource property to False.
        """
        self.optional = False

    def disable(self):
        """
        Set the disabled resource property to True.
        """
        self.disabled = True

    def enable(self):
        """
        Set the disabled resource property to False.
        """
        self.disabled = False

    def clear_cache(self, sig):
        """
        Wraps the rcUtilities clear_cache function, setting the resource
        as object keyword argument.
        """
        clear_cache(sig, o=self)

    def action_triggers(self, trigger, action, **kwargs):
        """
        Executes a resource trigger. Guess if the shell mode is needed from
        the trigger syntax.
        """
        action_triggers(self, trigger, action, **kwargs)

    def handle_confirm(self, action):
        """
        Tasks can require a run confirmation. We want the confirmation checked
        before executing triggers.
        """
        if not hasattr(self, "confirm"):
            return
        if action != "run":
            return
        self.confirm()

    def action_main(self, action):
        """
        Shortcut the resource action if in dry-run mode.
        """
        if self.svc.options.dry_run:
            if self.rset.parallel:
                header = "+ "
            else:
                header = ""
            self.log.info("%s%s %s", header, action, self.label)
            return
        getattr(self, action)()

    def do_action(self, action):
        """
        Call the resource action method if implemented.

        If the action is a stopping action and the resource is flagged
        standby on this node, skip.

        Call the defined pre and post triggers.

        """
        if not hasattr(self, action):
            self.log.debug("%s action is not implemented", action)

        self.log.debug('do action %s', action)

        if action == "stop" and self.standby and not self.svc.options.force:
            standby_action = action+'standby'
            if hasattr(self, standby_action):
                self.action_main(standby_action)
                return
            else:
                self.log.info("skip '%s' on standby resource (--force to override)", action)
                return

        self.check_requires(action)
        self.handle_confirm(action)
        self.setup_environ()
        self.action_triggers("pre", action)
        self.action_triggers("blocking_pre", action, blocking=True)
        self.action_main(action)
        self.action_triggers("post", action)
        self.action_triggers("blocking_post", action, blocking=True)
        if self.need_refresh_status(action):
            self.status(refresh=True)
        return

    def need_refresh_status(self, action):
        """
        Return True for action known to be causing a resource status change.
        """
        actions = (
            "start",
            "startstandby",
            "stop",
            "rollback",
            "unprovision",
            "provision",
            "install",
            "create",
            "switch",
            "migrate"
        )
        if self.svc.options.dry_run:
            return False
        if "sync" in action and self.type.startswith("sync"):
            return True
        if action in actions:
            return True
        return False

    def skip_resource_action(self, action):
        """
        Return True if the action should be skipped.
        """
        actions = (
            "start",
            "startstandby",
            "stop",
            "provision",
            "run",
            "set_unprovisioned",
            "set_provisioned",
            "unprovision",
        )
        if not self.skip:
            return False
        if action.startswith("sync"):
            return True
        if action.startswith("_pg_"):
            return True
        if action in actions:
            return True
        return False

    def action(self, action):
        """
        Try to call the resource do_action() if:
        * action is not None
        * the resource is not skipped by resource selectors

        If the resource is disabled, the return status depends on the optional
        property:
        * if optional, return True
        * else return do_action() return value
        """
        if self.rid != "app" and not self.svc.encap and self.encap:
            self.log.debug('skip encap resource action: action=%s res=%s', action, self.rid)
            return

        if 'noaction' in self.tags and \
           not hasattr(self, "delayed_noaction") and \
           action not in ALLOW_ACTION_WITH_NOACTION:
            self.log.debug('skip resource action %s (noaction tag)', action)
            return

        self.log.debug('action: %s', action)

        if action is None:
            self.log.debug('action: action cannot be None')
            return True
        if self.skip_resource_action(action):
            self.log.debug('action: skip action on filtered-out resource')
            return True
        if self.is_disabled():
            self.log.debug('action: skip action on disabled resource')
            return True
        if not hasattr(self, action):
            self.log.debug('action: not applicable (not implemented)')
            return True

        try:
            self.progress()
            self.do_action(action)
        except ex.excUndefined as exc:
            print(exc)
            return False
        except ex.excContinueAction as exc:
            self.log.info(str(exc))
        except ex.excError as exc:
            if self.optional:
                if len(str(exc)) > 0:
                    self.log.error(str(exc))
                self.log.info("ignore %s error on optional resource", action)
            else:
                raise

    def status_stdby(self, status):
        """
        This method modifies the passed status according to the standby
        property.
        """
        if not self.standby:
            return status
        if status == rcStatus.UP:
            return rcStatus.STDBY_UP
        elif status == rcStatus.DOWN:
            return rcStatus.STDBY_DOWN
        return status

    def try_status(self, verbose=False):
        """
        Catch status methods errors and push them to the resource log buffer
        so they will be display in print status.
        """
        try:
            return self._status(verbose=verbose)
        except Exception as exc:
            self.status_log(str(exc), "error")
            return rcStatus.UNDEF

    def _status(self, verbose=False):
        """
        The resource status evaluation method.
        To be implemented by drivers.
        """
        if verbose:
            self.log.debug("default resource status: undef")
        return rcStatus.UNDEF

    def force_status(self, status):
        """
        Force a resource status, bypassing the evaluation method.
        """
        self.rstatus = status
        self.status_logs = [("info", "forced")]
        self.write_status()

    def status(self, **kwargs):
        """
        Resource status evaluation method wrapper.
        Handles caching, nostatus tag and disabled flag.
        """
        verbose = kwargs.get("verbose", False)
        refresh = kwargs.get("refresh", False)
        ignore_nostatus = kwargs.get("ignore_nostatus", False)

        if self.is_disabled():
            return rcStatus.NA

        if not ignore_nostatus and "nostatus" in self.tags:
            self.status_log("nostatus tag", "info")
            return rcStatus.NA

        if self.rstatus is not None and not refresh:
            return self.rstatus

        last_status = self.load_status_last()

        if refresh:
            self.purge_status_last()
        else:
            self.rstatus = last_status

        # now the rstatus can no longer be None
        if self.rstatus == rcStatus.UNDEF or refresh:
            self.status_logs = []
            self.rstatus = self.try_status(verbose)
            self.rstatus = self.status_stdby(self.rstatus)
            self.log.debug("refresh status: %s => %s",
                           rcStatus.Status(last_status),
                           rcStatus.Status(self.rstatus))
            self.write_status()

        if self.rstatus in (rcStatus.UP, rcStatus.STDBY_UP) and \
           self.is_provisioned_flag() is False:
            self.write_is_provisioned_flag(True)

        return self.rstatus

    def write_status(self):
        """
        Helper method to janitor resource status cache and history in files.
        """
        self.write_status_last()
        self.write_status_history()

    @lazy
    def fpath_status_last(self):
        """
        Return the file path for the resource status cache.
        """
        return os.path.join(self.var_d, "status.last")

    @lazy
    def fpath_status_history(self):
        """
        Return the file path for the resource status history.
        """
        return os.path.join(self.var_d, "status.history")

    def purge_status_last(self):
        """
        Purge the on-disk resource status cache.
        """
        try:
            os.unlink(self.fpath_status_last)
        except:
            pass

    def has_status_last(self):
        return os.path.exists(self.fpath_status_last)

    def load_status_last(self):
        """
        Fetch the resource status from the on-disk cache.
        """
        try:
            with open(self.fpath_status_last, 'r') as ofile:
                lines = ofile.read().splitlines()
        except (OSError, IOError) as exc:
            if exc.errno != 2:
                # not EEXISTS
                self.log.debug(exc)
            return rcStatus.UNDEF

        try:
            status = rcStatus.Status(lines[0])
        except (IndexError, AttributeError, ValueError) as exc:
            self.log.debug(exc)
            return rcStatus.UNDEF

        self.status_logs = []
        if len(lines) > 1:
            for line in lines[1:]:
                if line.startswith("info: "):
                    self.status_logs.append(("info", line.replace("info: ", "", 1)))
                elif line.startswith("warn: "):
                    self.status_logs.append(("warn", line.replace("warn: ", "", 1)))
                elif line.startswith("error: "):
                    self.status_logs.append(("error", line.replace("error: ", "", 1)))
                else:
                    self.status_logs.append(("warn", line))

        return status

    def write_status_last(self):
        """
        Write the in-memory resource status to the on-disk cache.
        """
        status_str = "%s\n" % rcStatus.Status(self.rstatus)
        if len(self.status_logs) > 0:
            status_str += '\n'.join([entry[0]+": "+entry[1] for entry in self.status_logs])+'\n'
        dpath = os.path.dirname(self.fpath_status_last)
        if not os.path.exists(dpath):
            os.makedirs(dpath, 0o0755)
        with open(self.fpath_status_last, 'w') as ofile:
            ofile.write(status_str)
            ofile.flush()

    def write_status_history(self):
        """
        Log a change to the resource status history file.
        """
        fpath = self.fpath_status_history
        try:
            with open(fpath, 'r') as ofile:
                lines = ofile.readlines()
                last = lines[-1].split(" | ")[-1].strip("\n")
        except:
            last = None
        current = rcStatus.Status(self.rstatus)
        if current == last:
            return
        log = logging.getLogger("status_history")
        logformatter = logging.Formatter("%(asctime)s | %(message)s")
        logfilehandler = logging.handlers.RotatingFileHandler(
            fpath,
            maxBytes=512000,
            backupCount=1,
        )
        logfilehandler.setFormatter(logformatter)
        log.addHandler(logfilehandler)
        log.error(current)
        logfilehandler.close()
        log.removeHandler(logfilehandler)

    def status_log(self, text, level="warn"):
        """
        Add a message to the resource status log buffer, for
        display in the print status output.
        """
        if len(text) == 0:
            return
        if (level, text) in self.status_logs:
            return
        self.status_logs.append((level, text))

    def status_logs_get(self, levels=None):
        """
        Return filtered messages from the the resource status log buffer.
        """
        if levels is None:
            levels = ["info", "warn", "error"]
        return [entry[1] for entry in self.status_logs if \
                entry[0] in levels and entry[1] != ""]

    def status_logs_count(self, levels=None):
        """
        Return the number of status log buffer entries matching the
        specified levels.
        """
        if levels is None:
            levels = ["info", "warn", "error"]
        return len(self.status_logs_get(levels=levels))

    def status_logs_strlist(self):
        return ["%s: %s" % (lvl, msg) for (lvl, msg) in self.status_logs]

    def status_logs_str(self, color=False):
        """
        Returns the formatted resource status log buffer entries.
        """
        status_str = ""
        for level, text in self.status_logs:
            if len(text) == 0:
                continue
            entry = level + ": " + text + "\n"
            if color:
                if level == "warn":
                    color = rcColor.color.BROWN
                elif level == "error":
                    color = rcColor.color.RED
                else:
                    color = rcColor.color.LIGHTBLUE
                status_str += rcColor.colorize(entry, color)
            else:
                status_str += entry
        return status_str

    def call(self, *args, **kwargs):
        """
        Wrap rcUtilities call, setting the resource logger
        """
        kwargs["log"] = self.log
        return call(*args, **kwargs)

    def vcall(self, *args, **kwargs):
        """
        Wrap vcall, setting the resource logger
        """
        kwargs["log"] = self.log
        return vcall(*args, **kwargs)

    def lcall(self, *args, **kwargs):
        """
        Wrap lcall, setting the resource logger
        """
        kwargs["logger"] = self.log
        return lcall(*args, **kwargs)

    @staticmethod
    def wait_for_fn(func, tmo, delay, errmsg="Waited too long for startup"):
        """
        A helper function to execute a test function until it returns True
        or the number of retries is exhausted.
        """
        for tick in range(tmo//delay):
            if func():
                return
            time.sleep(delay)
        raise ex.excError(errmsg)

    def base_devs(self):
        """
        List devices the resource holds at the base of the dev tree.
        """
        devps = self.sub_devs() | self.exposed_devs()
        devs = self.svc.node.devtree.get_devs_by_devpaths(devps)
        base_devs = set()
        for dev in devs:
            top_devs = dev.get_top_devs()
            for dev in top_devs:
                base_devs.add(os.path.realpath(dev.devpath[0]))
        return base_devs

    def sub_devs(self):
        """
        List devices the resource holds.
        """
        return set()

    def exposed_devs(self):
        """
        List devices the resource exposes.
        """
        return set()

    def base_disks(self):
        """
        List disks the resource holds at the base of the dev tree. Some
        resource have none, and can leave this function as is.
        """
        devs = self.base_devs()
        try:
            u = __import__('rcUtilities'+rcEnv.sysname)
            disks = u.devs_to_disks(self, devs)
        except:
            disks = devs
        return disks

    def sub_disks(self):
        """
        List disks the resource holds. Some resource have none, and can leave
        this function as is.
        """
        devs = self.sub_devs()
        try:
            u = __import__('rcUtilities'+rcEnv.sysname)
            disks = u.devs_to_disks(self, devs)
        except:
            disks = devs
        return disks

    def exposed_disks(self):
        """
        List disks the resource exposes. Some resource have none, and can leave
        this function as is.
        """
        devs = self.exposed_devs()
        try:
            u = __import__('rcUtilities'+rcEnv.sysname)
            disks = u.devs_to_disks(self, devs)
        except:
            disks = devs
        return disks

    def presync(self):
        """
        A method called before a sync action is executed.
        """
        pass

    def postsync(self):
        """
        A method called after a sync action is executed.
        """
        pass

    @staticmethod
    def default_files_to_sync():
        """
        If files_to_sync() is not superceded, return an empty list as the
        default resource files to sync.
        """
        return []

    def files_to_sync(self):
        """
        Returns a list of files to contribute to sync#i0
        """
        return self.default_files_to_sync()

    def rollback(self):
        """
        Executes a resource stop if the resource start has marked the resource
        as rollbackable.
        """
        if self.can_rollback and not self.standby:
            self.stop()

    def stop(self):
        """
        The resource stop action entrypoint.
        """
        pass

    def startstandby(self):
        """
        Promote the action to start if the resource is flagged standby
        """
        if self.standby:
            self.start()

    def start(self):
        """
        The resource start action entrypoint.
        """
        pass

    def shutdown(self):
        """
        Always promote to the stop action
        """
        self.stop()

    def _pg_freeze(self):
        """
        Wrapper function for the process group freeze method.
        """
        return self._pg_freezer("freeze")

    def _pg_thaw(self):
        """
        Wrapper function for the process group thaw method.
        """
        return self._pg_freezer("thaw")

    def _pg_kill(self):
        """
        Wrapper function for the process group kill method.
        """
        return self._pg_freezer("kill")

    def _pg_freezer(self, action):
        """
        Wrapper function for the process group methods.
        """
        if not self.svc.create_pg:
            return
        if self.svc.pg is None:
            return
        if action == "freeze":
            self.svc.pg.freeze(self)
        elif action == "thaw":
            self.svc.pg.thaw(self)
        elif action == "kill":
            self.svc.pg.kill(self)

    def pg_frozen(self):
        """
        Return True if the resource has its process group frozen
        """
        if not self.svc.create_pg:
            return False
        if self.svc.pg is None:
            return False
        return self.svc.pg.frozen(self)

    def create_pg(self):
        """
        Create a process group if this service asks for it and if possible.
        """
        if not self.svc.create_pg:
            return
        if self.svc.pg is None:
            return
        self.svc.pg.create_pg(self)

    def check_requires(self, action):
        """
        Iterate the resource 'requires' definition, and validate each
        requirement.
        """
        param = action + "_requires"
        if not hasattr(self, param):
            return
        requires = getattr(self, param)
        if len(requires) == 0:
            return
        for element in requires:
            self._check_requires(element)

    def _check_requires(self, element):
        """
        Validate a requires element, raising excError if the requirement is
        not met.
        """
        if element is None:
            return
        if element == "impossible":
            raise ex.excContinueAction("skip impossible requirement")
        if element.count("(") == 1:
            rid, states = element.rstrip(")").split("(")
            states = states.split(",")
        else:
            rid = element
            states = ["up", "stdby up"]
        if rid not in self.svc.resources_by_id:
            self.log.warning("ignore requires on %s: resource not found", rid)
            return
        resource = self.svc.resources_by_id[rid]
        current_state = rcStatus.Status(resource.status())
        if current_state not in states:
            msg = "requires on resource %s in state %s, current state %s" % \
                  (rid, " or ".join(states), current_state)
            if self.svc.options.cron:
                raise ex.excContinueAction(msg)
            else:
                raise ex.excError(msg)

    def dns_update(self):
        """
        Placeholder for resource specific implementation of the dns update.
        """
        pass

    def conf_get(self, o, **kwargs):
        """
        Relay for the Svc::conf_get() method, setting the resource rid as the
        config section.
        """
        return self.svc.conf_get(self.rid, o, **kwargs)

    def status_info(self):
        return {}

    def info(self):
        data = [
          ["driver", self.type],
          ["standby", str(self.standby).lower()],
          ["optional", str(self.optional).lower()],
          ["disabled", str(self.disabled).lower()],
          ["monitor", str(self.monitor).lower()],
          ["shared", str(self.shared).lower()],
          ["encap", str(self.encap).lower()],
          ["restart", str(self.nb_restart)],
        ]
        if self.subset:
            data.append(["subset", self.subset])
        if len(self.tags) > 0:
            data.append(["tags", " ".join(self.tags)])
        if hasattr(self, "_info"):
            try:
                data += self._info()
            except Exception as e:
                print(e, file=sys.stderr)
        return self.fmt_info(data)

    ##########################################################################
    #
    # provisioning
    #
    ##########################################################################
    @lazy
    def provisioned_flag(self):
        """
        The full path to the provisioned state cache file.
        """
        return os.path.join(self.var_d, "provisioned")

    def provisioned_flag_mtime(self):
        """
        Return the provisioned state cache file modification time.
        """
        try:
            return os.path.getmtime(self.provisioned_flag)
        except Exception:
            return

    def provisioned_data(self):
        """
        Return the resource provisioned state from the on-disk cache and its
        state change time as a dictionnary.
        """
        return {
            "state": self.is_provisioned(),
            "mtime": self.provisioned_flag_mtime(),
        }

    def is_provisioned_flag(self):
        """
        Return the boolean provisioned state cached on disk.
        Return None if the file does not exist or is corrupted.
        """
        try:
            with open(self.provisioned_flag, 'r') as filep:
                return json.load(filep)
        except Exception:
            return

    def write_is_provisioned_flag(self, value, mtime=None):
        """
        Write a resource-private file containing the boolean provisioned
        state and state change time.
        """
        if value is None:
            return
        try:
            with open(self.provisioned_flag, 'w') as filep:
                try:
                    json.dump(value, filep)
                    filep.flush()
                except ValueError:
                    return
        except Exception:
            # can happen in instance delete codepath
            return
        if mtime:
            os.utime(self.provisioned_flag, (mtime, mtime))

    def remove_is_provisioned_flag(self):
        """
        Remove the provisioned state cache file. Used in the Svc::delete_resource()
        code path.
        """
        if not os.path.exists(self.provisioned_flag):
            return
        os.unlink(self.provisioned_flag)

    def set_unprovisioned(self):
        """
        Exposed resource action to force the provisioned state to False in the cache file.
        """
        self.log.info("set unprovisioned")
        self.write_is_provisioned_flag(False)

    def set_provisioned(self):
        """
        Exposed resource action to force the provisioned state to True in the cache file.
        """
        self.log.info("set provisioned")
        self.write_is_provisioned_flag(True)

    @lazy
    def prov(self):
        """
        Find the provisioning module, import it and instanciate a Prov object.
        """
        try:
            driver_group, driver = self.type.split(".", 1)
        except ValueError:
            driver_group = self.type
            driver = ""
        try:
            mod = mimport("prov", driver_group, driver, fallback=True)
        except ImportError as exc:
            mod = __import__("provisioning")
        if not hasattr(mod, "Prov"):
            raise ex.excError("missing Prov class in module %s" % str(mod))
        return getattr(mod, "Prov")(self)

    def provision(self):
        if self.shared and not self.svc.options.disable_rollback and \
           os.environ.get("OSVC_ACTION_ORIGIN") == "daemon":
            self.log.info("skip shared resource provisioning: not leader")
            self.write_is_provisioned_flag(True, mtime=1)
            return
        self._provision()
        self.prov.start()

    def _provision(self):
        """
        Unimplemented is_provisioned() trusts provisioner() to do the right
        thing.
        """
        if self.skip_provision:
            self.log.info("provision skipped (configuration directive)")
            return
        if self.prov is None:
            return
        if hasattr(self, "refresh_provisioned_on_provision"):
            refresh = self.refresh_provisioned_on_provision
        else:
            refresh = False
        if self.is_provisioned(refresh=refresh) is True:
            self.log.info("%s already provisioned", self.label)
        else:
            self.prov.provisioner()
        self.write_is_provisioned_flag(True)

    def unprovision(self):
        self.prov.stop()
        self._unprovision()

    def _unprovision(self):
        """
        Unimplemented is_provisioned() trusts unprovisioner() to do the right
        thing.
        """
        if self.skip_provision or self.skip_unprovision:
            self.log.info("unprovision skipped (configuration directive)")
            return
        if self.prov is None:
            return
        if hasattr(self, "refresh_provisioned_on_unprovision"):
            refresh = self.refresh_provisioned_on_unprovision
        else:
            refresh = False
        if self.is_provisioned(refresh=refresh) is False:
            self.log.info("%s already unprovisioned", self.label)
        else:
            self.prov.unprovisioner()
        self.write_is_provisioned_flag(False)

    def is_provisioned(self, refresh=False):
        if self.prov is None:
            return True
        if not refresh:
            flag = self.is_provisioned_flag()
            if flag is not None:
                return flag
        if not hasattr(self.prov, "is_provisioned"):
            return
        value = self.prov.is_provisioned()
        if not self.shared or self.svc.options.disable_rollback or \
           (self.shared and not refresh and value):
            self.write_is_provisioned_flag(value)
        return value

    def promote_rw(self):
        if not self.need_promote_rw:
            return
        mod = __import__("rcUtilities"+rcEnv.sysname)
        if not hasattr(mod, "promote_dev_rw"):
            self.log.warning("promote_rw is not supported on this operating system")
            return
        for dev in self.base_devs():
            getattr(mod, "promote_dev_rw")(dev, log=self.log)

    def progress(self):
        lock.progress(self.svc.lockfd, {"rid": self.rid})

    def unset_lazy(self, prop):
        """
        Expose the self.unset_lazy(...) utility function as a method,
        so Node() users don't have to import it from rcUtilities.
        """
        unset_lazy(self, prop)

    def reslock(self, action=None, timeout=30, delay=1, suffix=None):
        """
        Acquire the resource action lock.
        """
        if self.lockfd is not None:
            # already acquired
            return

        lockfile = os.path.join(rcEnv.paths.pathlock, self.svc.svcname+"."+self.rid)
        if suffix is not None:
            lockfile = ".".join((lockfile, suffix))

        details = "(timeout %d, delay %d, action %s, lockfile %s)" % \
                  (timeout, delay, action, lockfile)
        self.log.debug("acquire resource lock %s", details)

        try:
            lockfd = lock.lock(
                timeout=timeout,
                delay=delay,
                lockfile=lockfile,
                intent=action
            )
        except lock.LockTimeout as exc:
            raise ex.excError("timed out waiting for lock %s: %s" % (details, str(exc)))
        except lock.LockNoLockFile:
            raise ex.excError("lock_nowait: set the 'lockfile' param %s" % details)
        except lock.LockCreateError:
            raise ex.excError("can not create lock file %s" % details)
        except lock.LockAcquire as exc:
            raise ex.excError("another action is currently running %s: %s" % (details, str(exc)))
        except ex.excSignal:
            raise ex.excError("interrupted by signal %s" % details)
        except Exception as exc:
            self.save_exc()
            raise ex.excError("unexpected locking error %s: %s" % (details, str(exc)))

        if lockfd is not None:
            self.lockfd = lockfd

    def resunlock(self):
        """
        Release the service action lock.
        """
        lock.unlock(self.lockfd)
        self.lockfd = None


   0707010005ca08000081b40000000000000000000000015ab2175400003428000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/resourceset.py """
Defines the resource set class, which is a collection of resources.
"""
from __future__ import print_function

import os
import sys
import logging

import rcExceptions as ex
import rcStatus
from rcUtilities import lazy
from rcGlobalEnv import rcEnv
from resources import Resource

class ResourceSet(object):
    """
    Define a set of resources of the same type.
    Example: ResourceSet("fs", [m1, m2])
    """
    def __init__(self,
                 rid=None,
                 resources=None,
                 parallel=False,
                 optional=False,
                 disabled=False,
                 tags=None):
        self.parallel = parallel
        self.svc = None
        self.rid = rid
        self.optional = optional
        self.disabled = disabled
        self.tags = tags
        self.resources = []
        if resources is not None:
            for resource in resources:
                self += resource

    def __lt__(self, other):
        return self.rid < other.rid

    def __iadd__(self, other):
        """
        Add a resource to the resourceset.

        Example 1: iadd another ResourceSet: R+=ResSet ... R+=[m1,m2]
        Example 2: iadd a single Resource: R+=ip1
        """
        if isinstance(other, ResourceSet):
            self.resources.extend(other.resources)
        elif isinstance(other, Resource):
            # setup a back pointer to the resource set
            other.rset = self
            self.resources.append(other)
            if hasattr(other, 'sort_rset'):
                other.sort_rset(self)
        return self

    def __str__(self):
        output = "resSet %s [" % str(self.rid)
        for resource in self.resources:
            output += " (%s)" % (resource.__str__())
        return "%s]" % output

    def __iter__(self):
        for resource in self.resources:
            yield(resource)

    def pre_action(self, action, **kwargs):
        """
        Call the pre_action of each resource driver in the resource set.
        """
        tags = kwargs.get("tags", set())
        xtags = kwargs.get("xtags", set())
        xtypes = kwargs.get("xtypes")
        types = kwargs.get("types")
        resources = self.action_resources(action, tags, xtags, xtypes, types)
        if len(resources) == 0:
            return []
        types_done = []
        types_aborted = []
        for resource in resources:
            if resource.type in types_done:
                continue
            types_done.append(resource.type)
            if not hasattr(resource, "pre_action"):
                continue
            try:
                resource.pre_action(action)
            except ex.excAbortAction:
                types_aborted.append(resource.type)
        return types_aborted

    def post_action(self, action, **kwargs):
        """
        Call the post_action of each resource driver in the resource set.
        """
        tags = kwargs.get("tags", set())
        xtags = kwargs.get("xtags", set())
        xtypes = kwargs.get("xtypes")
        types = kwargs.get("types")
        resources = self.action_resources(action, tags, xtags, xtypes, types)
        if len(resources) == 0:
            return []
        types_done = []
        types_aborted = []
        for resource in resources:
            if resource.type in types_done:
                continue
            types_done.append(resource.type)
            if not hasattr(resource, "post_action"):
                continue
            try:
                resource.post_action(action)
            except ex.excAbortAction:
                types_aborted.append(resource.type)
        return types_aborted

    def purge_status_last(self):
        """
        Purge the on-disk status cache of each resource of the resourceset.
        """
        for resource in self.resources:
            resource.purge_status_last()

    def is_disabled(self):
        if self.svc.disabled:
            return self.svc.disabled
        return self.disabled

    def status(self, **kwargs):
        """
        Return the aggregate status a ResourceSet.
        """
        agg_status = rcStatus.Status()
        for resource in self.resources:
            if resource.is_disabled():
                continue
            if not self.svc.encap and resource.encap:
                # don't evaluate encap service resources
                continue

            try:
                status = resource.status(**kwargs)
            except:
                import traceback
                exc = sys.exc_info()
                print(exc[0], exc[1], traceback.print_tb(exc[2]))
                status = rcStatus.NA

            agg_status += status
        return agg_status.status

    @staticmethod
    def tag_match(rtags, keeptags):
        """
        A helper method to determine if resource has a tag in the specified
        list of tags.
        """
        if len(keeptags) == 0:
            return True
        for tag in rtags:
            if tag in keeptags:
                return True
        return False

    def has_resource_with_types(self, types, strict=False):
        """
        Return True if the resourceset has at least one resource of the
        specified type.
        """
        for resource in self.resources:
            if resource.type in types:
                return True
            if not strict and "." in resource.type and \
               resource.type.split(".")[0] in types:
                return True
        return False

    def has_encap_resources(self):
        """
        Return True if the resourceset has at least one encap resource
        """
        resources = [True for res in self.resources if resource.encap]
        if len(resources) == 0:
            return False
        return True

    def sort_resources(self, resources, action):
        """
        Return resources after a resourceset-specific sort.
        To be implemented by child classes if desired.
        """
        if action in ["start", "startstandby", "provision"] or \
           self.rid.startswith("sync"):
            resources.sort()
        else:
            resources.sort(reverse=True)
        return resources

    def action_resources(self, action, tags, xtags, xtypes, types):
        """
        Return resources to execute the action on.
        """
        if len(xtags) > 0:
            resources = []
            for res in self.resources:
                if not self.tag_match(res.tags, xtags):
                    resources.append(res)
                    continue
                if self.svc.command_is_scoped() and \
                   res.rid in self.svc.action_rid_before_depends and \
                   len(self.svc.action_rid_dependencies(action, res.rid)) == 0:
                    resources.append(res)
                    continue
        else:
            resources = self.resources

        resources = [res for res in resources if self.tag_match(res.tags, tags)]
        self.log.debug("resources after tags[%s] filter: %s",
                       ','.join(tags),
                       ','.join([res.rid for res in resources]))

        disabled_rids = [res.rid for res in resources if res.is_disabled() and not res.skip]
        if len(disabled_rids) > 0:
            self.log.info("%s disabled", ",".join(disabled_rids))

        resources = [res for res in resources if not res.is_disabled()]
        self.log.debug("resources after 'disable' filter: %s",
                       ','.join([res.rid for res in resources]))

        resources = [res for res in resources if \
                     types is None or \
                     res.type in types or \
                     res.type.split(".")[0] in types]
        resources = [res for res in resources if \
                     xtypes is None or \
                     (res.type not in xtypes and \
                      res.type.split(".")[0] not in xtypes)]

        if action == "startstandby":
            # filter out resource not in standby mode
            resources = [res for res in resources if res.standby]

        resources = self.sort_resources(resources, action)
        return resources

    def action(self, action, **kwargs):
        """
        Call the action method for each resource of the ResourceSet.
        Handle parallel or serialized execution plans.
        """
        tags = kwargs.get("tags", set())
        xtags = kwargs.get("xtags", set())
        xtypes = kwargs.get("xtypes")
        types = kwargs.get("types")

        if self.parallel:
            # verify we can actually do parallel processing, fallback to serialized
            try:
                from multiprocessing import Process
                if rcEnv.sysname == "Windows":
                    from multiprocessing import set_executable
                    set_executable(os.path.join(sys.exec_prefix, 'pythonw.exe'))
            except:
                self.parallel = False

        resources = self.action_resources(action, tags, xtags, xtypes, types)
        barrier = None

        if not self.svc.options.dry_run and \
           self.parallel and len(resources) > 1 and \
           action not in ["presync", "postsync"]:
            procs = {}
            for resource in resources:
                if not resource.can_rollback and action == "rollback":
                    continue
                if resource.skip or resource.is_disabled():
                    continue
                proc = Process(target=self.action_job, args=(resource, action,))
                proc.start()
                resource.log.info("action %s started in child process %d"%(action, proc.pid))
                procs[resource.rid] = proc
                if self.svc.options.upto and resource.rid == self.svc.options.upto:
                    barrier = "reached 'up to %s' barrier" % resource.rid
                    break
                if self.svc.options.downto and resource.rid == self.svc.options.downto:
                    barrier = "reached 'down to %s' barrier" % resource.rid
                    break
            for proc in procs.values():
                proc.join()
            err = []
            for resource in resources:
                if resource.rid not in procs:
                    continue
                proc = procs[resource.rid]
                if proc.exitcode == 1 and not resource.optional:
                    err.append(resource.rid)
                elif proc.exitcode == 2:
                    # can_rollback resource property is lost with the thread
                    # the action_job tells us what to do with it through its exitcode
                    resource.can_rollback = True
            if len(err) > 0:
                raise ex.excError("%s non-optional resources jobs returned "
                                  "with error" % ",".join(err))
        else:
            if self.svc.options.dry_run and \
               self.parallel and len(resources) > 1 and \
               action not in ["presync", "postsync"]:
                self.log.info("entering parallel subset")
            for resource in resources:
                try:
                    resource.action(action)
                except ex.excAbortAction as exc:
                    msg = str(exc)
                    if msg is not "":
                        resource.log.warning(msg)
                    resource.log.warning("abort action on resource set")
                    break
                except ex.excContinueAction as exc:
                    msg = str(exc)
                    if msg is not "":
                        resource.log.info(msg)
                    resource.log.info("continue action on resource set")
                except ex.excError as exc:
                    msg = str(exc)
                    if msg is not "":
                        resource.log.error(msg)
                        # prevent re-logging
                        exc.value = ""
                    raise exc
                if self.svc.options.upto and resource.rid == self.svc.options.upto:
                    barrier = "reached 'up to %s' barrier" % resource.rid
                    break
                if self.svc.options.downto and resource.rid == self.svc.options.downto:
                    barrier = "reached 'down to %s' barrier" % resource.rid
                    break

        if barrier:
            raise ex.excEndAction(barrier)

    def action_job(self, resource, action):
        """
        The worker job used for parallel execution of a resource action in
        a resource set.
        """
        try:
            getattr(resource, 'action')(action)
        except Exception as exc:
            self.log.error(str(exc))
            sys.exit(1)
        if resource.can_rollback:
            sys.exit(2)
        sys.exit(0)

    def all_skip(self, action):
        """
        Return False if any resource will not skip the action.
        """
        for resource in self.resources:
            if not resource.skip_resource_action(action):
                return False
        return True

    @lazy
    def log(self):
        """
        Lazy init for the resource logger.
        """
        name = ".".join((
            rcEnv.nodename,
            self.svc.svcname,
            self.rid
        ))
        return logging.getLogger(name)

if __name__ == "__main__":
    pass
0707010005ca09000081b40000000000000000000000015ab21754000010e2000000660000000000000000000000000000002300000000root/usr/share/opensvc/lib/snap.py    import os
import resources as Res
import rcExceptions as ex


def find_mount(rs, dir):
    """Sort mounts from deepest to shallowest and return the
       first mount whose 'mount_point' is matching 'dir'
    """
    for m in sorted(rs.resources, reverse=True):
        if m.is_disabled():
            continue
        if m.mount_point in dir:
            return m
    return None

def find_mounts(self, mounts_h):
    rs = self.svc.get_resourcesets("fs")[0]
    if rs is None:
        self.log.error("can not find fs resources encapsulating %s to snap (no fs resources)"%self.src)
        raise ex.syncNotSnapable
    for src in self.src:
        m = find_mount(rs, src)
        if m is None:
            self.log.error("can not find fs resources encapsulating %s to snap"%src)
            raise ex.syncNotSnapable
        mounts_h[src] = m
    return mounts_h

class Snap(Res.Resource):
    """Defines a snap object
    """
    def __init__(self, rid, optional=False, disabled=False, tags=set()):
        self.snaps = {}
        Res.Resource.__init__(self, rid, "sync.snap", optional=optional,\
                            disabled=disabled, tags=tags)

    def try_snap(self, rset, action, rid=None):
        if action == "nodes":
            action = "sync_nodes"
        if action == "drpnodes":
            action = "sync_drp"

        mounts_h = {}
        for r in rset.resources:
            """ if rid is set, snap only the specified resource.
                Used by resources tagged 'delay_snap' on sync()

                if rid is not set, don't snap resources tagged 'delay_snap'
                (pre_action() code path)
            """
            if rid is None:
                if "delay_snap" in r.tags:
                    continue
            elif rid != r.rid:
                continue

            if r.is_disabled():
                continue

            if r.snap is not True and r.snap is not False:
                self.log.error("service configuration error: 'snap' must be 'true' or 'false'. default is 'false'")
                raise ex.syncConfigSyntaxError

            if not r.snap:
                continue

            if (action == "sync_nodes" and not 'nodes' in r.target) or \
               (action == "sync_drp" and not 'drpnodes' in r.target):
                self.log.debug("action %s but resource target is %s"%(action, r.target))
                continue

            mounts_h = find_mounts(r, mounts_h)

        mounts = set(mounts_h.values())
        for m in mounts:
            try:
                self.snapcreate(m)
            except ex.syncNotSnapable:
                self.log.error("Resource not snapable: "+m.__str__())
                continue
            except (ex.syncNotSnapable, ex.syncSnapExists, ex.syncSnapMountError,
                ex.syncSnapCreateError, ex.syncSnapDestroyError):
                """Clean up the mess
                """
                self.snap_cleanup(rset)
                raise ex.excError
            except:
                raise

        """Update src dirs of every sync resource to point to an
           existing snap
        """
        for i, r in enumerate(rset.resources):
            r.alt_src = list(r.src)
            for j, src in enumerate(r.alt_src):
                if src not in mounts_h:
                    continue
                mnt = mounts_h[src].mount_point
                if mnt not in self.snaps:
                    continue
                snap_mnt = self.snaps[mnt]['snap_mnt']
                rset.resources[i].alt_src[j] = src.replace(os.path.join(mnt), os.path.join(snap_mnt), 1)

    def snap_cleanup(self, rset=None):
        if not hasattr(self, 'snaps'):
            return
        if len(self.snaps) == 0 :
            return
        for s in self.snaps.keys():
            self.snapdestroykey(s)
        if rset is None:
            return
        for i, r in enumerate(rset.resources):
            if hasattr(rset.resources[i], 'alt_src'):
                delattr(rset.resources[i], 'alt_src')

    def snapcreate(self, m):
        """ create a snapshot for m
        add self.snaps[m] with
            dict(snapinfo key val)
        """
        raise ex.MissImpl

    def snapdestroykey(self, snaps_key):
        """ destroy a snapshot for a snap key
        """
        raise ex.MissImpl

  0707010005ca0a000081b40000000000000000000000015ab2175400000c89000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/snapAdvfsOSF1.py   import os
from rcUtilities import justcall, protected_mount
import rcExceptions as ex
import snap
import rcAdvfs
from rcMountsOSF1 import Mounts
from rcGlobalEnv import rcEnv

class Snap(snap.Snap):
    """Defines a snap object with ZFS
    """

    def snapcreate(self, m):
        """ create a snapshot for m
        add self.snaps[m] with
            dict(snapinfo key val)
        """
        dom, fset = m.device.split('#')
        o = rcAdvfs.Fdmns()
        try:
            d = o.get_fdmn(dom)
        except rcAdvfs.ExInit:
            raise ex.syncNotSnapable
        if fset not in d.fsets:
            raise ex.syncNotSnapable
        clonefset = fset +'@osvc_sync'
        mount_point = m.mount_point
        snap_mount_point = os.path.join(rcEnv.paths.pathtmp, 'clonefset/%s/%s/osvc_sync'%(m.svc.svcname,mount_point))
        snap_mount_point = os.path.normpath(snap_mount_point)
        if not os.path.exists(snap_mount_point):
            try:
                os.makedirs(snap_mount_point)
                self.log.info('create directory %s'%snap_mount_point)
            except:
                self.log.error('failed to create directory %s'%snap_mount_point)
                raise ex.syncSnapCreateError
        clonedev = '#'.join((dom, clonefset))
        if Mounts().has_mount(clonedev, snap_mount_point):
            cmd = ['fuser', '-kcv', snap_mount_point]
            (ret, out, err) = self.vcall(cmd, err_to_info=True)
            cmd = ['umount', snap_mount_point]
            (ret, out, err) = self.vcall(cmd)
            if ret != 0:
                raise ex.excError
        if clonefset in d.fsets:
            (ret, buff, err) = self.vcall(['rmfset', '-f', dom, clonefset])
            if ret != 0:
                raise ex.syncSnapDestroyError
        (ret, buff, err) = self.vcall(['clonefset', dom, fset, clonefset])
        if ret != 0:
            raise ex.syncSnapCreateError
        (ret, buff, err) = self.vcall(['mount', '-t', 'advfs', clonedev, snap_mount_point])
        if ret != 0:
            raise ex.syncSnapCreateError
        self.snaps[mount_point]={'snap_mnt' : snap_mount_point, \
                                'snapdev' : clonedev }

    def snapdestroykey(self, snap_key):
        """ destroy a snapshot for a mount_point
        """
        clonedev = self.snaps[snap_key]['snapdev']
        dom, clonefset = clonedev.split('#')
        o = rcAdvfs.Fdmns()
        try:
            d = o.get_fdmn(dom)
        except rcAdvfs.ExInit:
            raise ex.syncSnapDestroyError
        if clonefset not in d.fsets:
            return

        if protected_mount(self.snaps[snap_key]['snap_mnt']):
            self.log.error("the clone fset is no longer mounted in %s. panic."%self.snaps[snap_key]['snap_mnt'])
            raise ex.excError
        cmd = ['fuser', '-kcv', self.snaps[snap_key]['snap_mnt']]
        (ret, out, err) = self.vcall(cmd, err_to_info=True)
        cmd = ['umount', self.snaps[snap_key]['snap_mnt']]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.excError

        (ret, buff, err) = self.vcall(['rmfset', '-f', dom, clonefset])
        if ret != 0:
            raise ex.syncSnapDestroyError
   0707010005ca0b000081b40000000000000000000000015ab217540000100b000000660000000000000000000000000000002a00000000root/usr/share/opensvc/lib/snapJfs2AIX.py import os

from rcGlobalEnv import rcEnv
from rcUtilities import which, qcall, protected_mount
import rcExceptions as ex
import snap

class Snap(snap.Snap):
    def lv_exists(self, device):
        device = device.split("/")[-1]
        ret = qcall(['lslv', device], cache=True)
        if ret == 0:
            return True
        return False

    def lv_info(self, device):
        device = device.split("/")[-1]
        (ret, buff, err) = self.call(['lslv', device], cache=True)
        if ret != 0:
            return (None, None, None)
        vg_name = None
        lv_name = None
        lv_size = 0
        prev = ''
        prevprev = ''
        pp_unit = ''
        pps = 0
        pp_size = 0
        for word in buff.split():
            if prev == "GROUP:":
                vg_name = word
            if prev == "VOLUME:":
                lv_name = word
            if prev == "SIZE:":
                pp_size = int(word)
            if prevprev == "SIZE:":
                pp_unit = word
            if prev == "PPs:" and prevprev != "STALE":
                pps = int(word)
            prevprev = prev
            prev = word

        if pps == 0 or pp_size == 0 or pp_unit == '' or vg_name is None:
            self.log.error("logical volume %s information fetching error"%device)
            print("pps = ", pps)
            print("pp_size = ", pp_size)
            print("pp_unit = ", pp_unit)
            print("vg_name = ", vg_name)
            raise ex.excError

        if pp_unit == 'megabyte(s)':
            mult = 1
        elif pp_unit == 'gigabyte(s)':
            mult = 1024
        elif pp_unit == 'terabyte(s)':
            mult = 1024*1024
        else:
            self.log.error("unexpected logical volume PP size unit: %s"%pp_unit)
            raise ex.excError

        return (vg_name, lv_name, pps*pp_size*mult)

    def snapcreate(self, m):
        snap_name = ''
        snap_mnt = ''
        (vg_name, lv_name, lv_size) = self.lv_info(m.device)
        if lv_name is None:
            self.log.error("can not snap %s: not a logical volume"%m.device)
            raise ex.syncNotSnapable
        if len(lv_name) > 12:
            self.log.error("can not snap lv with name >12 chars")
            raise ex.excError
        snap_name = 'sy_'+os.path.basename(lv_name)
        if self.lv_exists(os.path.join(vg_name, snap_name)):
            self.log.error("snap of %s already exists"%(lv_name))
            raise ex.syncSnapExists
        print(lv_size)
        print(lv_size//10)
        (ret, buff, err) = self.vcall(['mklv', '-t', 'jfs2', '-y', snap_name, vg_name, str(lv_size//10)+'M'])
        if ret != 0:
            raise ex.syncSnapCreateError
        snap_mnt = '/service/tmp/osvc_sync_'+os.path.basename(vg_name)+'_'+os.path.basename(lv_name)
        if not os.path.exists(snap_mnt):
            os.makedirs(snap_mnt, 0o755)
        snap_dev = os.path.join(os.sep, 'dev', snap_name)
        (ret, buff, err) = self.vcall(['snapshot', '-o', 'snapfrom='+m.mount_point, snap_dev])
        if ret != 0:
            raise ex.syncSnapMountError
        (ret, buff, err) = self.vcall(['mount', '-o', 'snapshot', snap_dev, snap_mnt])
        if ret != 0:
            raise ex.syncSnapMountError
        self.snaps[m.mount_point] = dict(lv_name=lv_name,
                                        vg_name=vg_name,
                                        snap_name=snap_name,
                                        snap_mnt=snap_mnt,
                                        snap_dev=snap_dev)

    def snapdestroykey(self, s):
        if protected_mount(self.snaps[s]['snap_mnt']):
            self.log.error("the snapshot is no longer mounted in %s. panic."%self.snaps[s]['snap_mnt'])
            raise ex.excError

        """ fuser on HP-UX outs to stderr ...
        """
        cmd = ['fuser', '-c', '-x', '-k', self.snaps[s]['snap_mnt']]
        ret = qcall(cmd)

        cmd = ['umount', self.snaps[s]['snap_mnt']]
        (ret, out, err) = self.vcall(cmd)
        cmd = ['snapshot', '-d', self.snaps[s]['snap_dev']]
        (ret, buff, err) = self.vcall(cmd)

 0707010005ca0c000081b40000000000000000000000015ab2175400001198000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/snapLvmLinux.py    import os

from rcGlobalEnv import rcEnv
from rcUtilities import protected_mount, justcall, which
from rcUtilitiesLinux import lv_info, lv_exists, udevadm_settle
import rcExceptions as ex
import snap

class Snap(snap.Snap):
    def mntopt_and_ro(self, m):
        opt_set = set()
        if m.fs_type == "xfs":
            opt_set.add("nouuid")
        if m.mount_options is None:
            opt_set.add("ro")
            return ','.join(opt_set)
        opt_set |= set(m.mount_options.split(','))
        opt_set -= set(['rw', 'ro'])
        opt_set |= set(['ro'])
        return ','.join(opt_set)

    def snapcreate(self, m):
        snap_name = ''
        snap_mnt = ''
        (vg_name, lv_name, lv_size) = lv_info(self, m.device)
        if lv_name is None:
            self.log.error("can not snap %s: not a logical volume"%m.device)
            raise ex.syncNotSnapable
        snap_name = 'osvc_sync_'+lv_name
        if lv_exists(self, os.path.join(os.sep, 'dev', vg_name, snap_name)):
            self.log.error("snap of %s already exists"%(lv_name))
            raise ex.syncSnapExists

        if m.snap_size is not None:
            snap_size = m.snap_size
        else:
            snap_size = int(lv_size//10)

        cmd = ['lvcreate', '-A', 'n', '-s', '-L'+str(snap_size)+'M', '-n', snap_name, os.path.join(vg_name, lv_name)]
        self.log.info(' '.join(cmd))
        out, err, ret = justcall(cmd)
        err_l1 = err.split('\n')
        err_l2 = []
        out_l = out.split('\n')
        for e in err_l1:
            if 'This metadata update is NOT backed up' in e:
                pass
            else:
                err_l2.append(e)
        err = '\n'.join(err_l2)
        out = '\n'.join(out_l)
        if len(out) > 0:
            self.log.info(out)
        if len(err) > 0:
            self.log.error(err)
        if ret != 0:
            raise ex.syncSnapCreateError
        snap_mnt = os.path.join(rcEnv.paths.pathtmp,
                                'osvc_sync_'+vg_name+'_'+lv_name)
        if not os.path.exists(snap_mnt):
            os.makedirs(snap_mnt, 0o755)
        snap_dev = os.path.join(os.sep, 'dev', vg_name, snap_name)
        if m.fs_type != "xfs":
            self.vcall(['fsck', '-a', snap_dev], err_to_warn=True)
        (ret, buff, err) = self.vcall([rcEnv.syspaths.mount, '-t', m.fs_type, '-o', self.mntopt_and_ro(m), snap_dev, snap_mnt])
        if ret != 0:
            self.vcall([rcEnv.syspaths.mount])
            self.vcall(["fuser", "-v", snap_mnt])
            self.vcall(['lvremove', '-A', 'n', '-f', snap_dev])
            raise ex.syncSnapMountError
        self.snaps[m.mount_point] = dict(lv_name=lv_name,
                                        vg_name=vg_name,
                                        snap_name=snap_name,
                                        snap_mnt=snap_mnt,
                                        snap_dev=snap_dev)

    def snapdestroykey(self, s):
        if protected_mount(self.snaps[s]['snap_mnt']):
            self.log.error("the snapshot is no longer mounted in %s. panic."%self.snaps[s]['snap_mnt'])
            raise ex.excError
        cmd = ['fuser', '-kmv', self.snaps[s]['snap_mnt']]
        (ret, out, err) = self.vcall(cmd, err_to_info=True)
        cmd = [rcEnv.syspaths.umount, self.snaps[s]['snap_mnt']]
        (ret, out, err) = self.vcall(cmd)

        udevadm_settle()
        cmd = ['lvremove', '-A', 'n', '-f', self.snaps[s]['snap_dev']]
        self.log.info(' '.join(cmd))
        for i in range(1, 30):
            out, err, ret = justcall(cmd)
            if ret == 0:
                break
        err_l1 = err.split('\n')
        err_l2 = []
        out_l = out.split('\n')
        for e in err_l1:
            if 'This metadata update is NOT backed up' in e:
                pass
            elif 'Falling back to direct link removal.' in e:
                out_l.append(e)
            elif 'Falling back to direct node removal.' in e:
                out_l.append(e)
            else:
                err_l2.append(e)
        err = '\n'.join(err_l2)
        out = '\n'.join(out_l)
        if len(out) > 0:
            self.log.info(out)
        if len(err) > 0:
            self.log.error(err)
        if ret != 0:
            self.log.error("failed to remove snapshot %s (attempts: %d)"%(self.snaps[s]['snap_dev'], i))
        elif i > 1:
            self.log.info("successfully removed snapshot %s (attempts: %d)"%(self.snaps[s]['snap_dev'], i))
        del(self.snaps[s])

0707010005ca0d000081b40000000000000000000000015ab2175400000abd000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/snapVxfsHP-UX.py   import os

from rcGlobalEnv import rcEnv
from rcUtilities import which, qcall, protected_mount
import rcExceptions as ex
import snap

class Snap(snap.Snap):
    def lv_exists(self, device):
        if qcall(['lvdisplay', device]) == 0:
            return True
        return False

    def lv_info(self, device):
        (ret, buff, err) = self.call(['lvdisplay', device])
        if ret != 0:
            return (None, None, None)
        vg_name = None
        lv_name = None
        lv_size = 0
        for line in buff.split('\n'):
            if "VG Name" in line:
                vg_name = line.split()[-1]
            if "LV Name" in line:
                lv_name = line.split()[-1]
            if "LV Size" in line:
                lv_size = int(line.split()[-1])
        return (vg_name, lv_name, lv_size)

    def snapcreate(self, m):
        snap_name = ''
        snap_mnt = ''
        (vg_name, lv_name, lv_size) = self.lv_info(m.device)
        if lv_name is None:
            self.log.error("can not snap %s: not a logical volume"%m.device)
            raise ex.syncNotSnapable
        snap_name = 'osvc_sync_'+os.path.basename(lv_name)
        if self.lv_exists(os.path.join(vg_name, snap_name)):
            self.log.error("snap of %s already exists"%(lv_name))
            raise ex.syncSnapExists
        (ret, buff, err) = self.vcall(['lvcreate', '-L', str(lv_size//10)+'M', '-n', snap_name, vg_name])
        if ret != 0:
            raise ex.syncSnapCreateError
        snap_mnt = '/service/tmp/osvc_sync_'+os.path.basename(vg_name)+'_'+os.path.basename(lv_name)
        if not os.path.exists(snap_mnt):
            os.makedirs(snap_mnt, 0o755)
        snap_dev = os.path.join(vg_name, snap_name)
        (ret, buff, err) = self.vcall(['mount', '-F', 'vxfs', '-o', 'ro,snapof='+m.device, snap_dev, snap_mnt])
        if ret != 0:
            raise ex.syncSnapMountError
        self.snaps[m.mount_point] = dict(lv_name=lv_name,
                                        vg_name=vg_name,
                                        snap_name=snap_name,
                                        snap_mnt=snap_mnt,
                                        snap_dev=snap_dev)

    def snapdestroykey(self, s):
        if protected_mount(self.snaps[s]['snap_mnt']):
            self.log.error("the snapshot is no longer mounted in %s. panic."%self.snaps[s]['snap_mnt'])
            raise ex.excError

        """ fuser on HP-UX outs to stderr ...
        """
        cmd = ['fuser', '-kc', self.snaps[s]['snap_mnt']]
        ret = qcall(cmd)

        cmd = ['umount', self.snaps[s]['snap_mnt']]
        (ret, out, err) = self.vcall(cmd)
        cmd = ['lvremove', '-f', self.snaps[s]['snap_dev']]
        (ret, buff, err) = self.vcall(cmd)

   0707010005ca0e000081b40000000000000000000000015ab21754000005a5000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/snapZfsSunOS.py    from rcUtilities import justcall
from rcGlobalEnv import rcEnv
import rcExceptions as ex
import snap
from rcZfs import dataset_exists

class Snap(snap.Snap):
    """Defines a snap object with ZFS
    """

    def snapcreate(self, m):
        """ create a snapshot for m
        add self.snaps[m] with
            dict(snapinfo key val)
        """
        dataset = m.device
        if not dataset_exists(dataset, 'filesystem'):
            raise ex.syncNotSnapable
        snapdev = dataset +'@osvc_sync'
        mount_point = m.mount_point
        snap_mount_point= mount_point + '/.zfs/snapshot/osvc_sync/'
        if dataset_exists(snapdev, 'snapshot'):
            (ret, buff, err) = self.vcall([rcEnv.syspaths.zfs, 'destroy', snapdev ])
            if ret != 0:
                raise ex.syncSnapDestroyError
        (ret, buff, err) = self.vcall([rcEnv.syspaths.zfs, 'snapshot', snapdev ])
        if ret != 0:
            raise ex.syncSnapCreateError
        self.snaps[mount_point]={'snap_mnt' : snap_mount_point, \
                                'snapdev' : snapdev }

    def snapdestroykey(self, snap_key):
        """ destroy a snapshot for a mount_point
        """
        snapdev = self.snaps[snap_key]['snapdev']
        if not dataset_exists(snapdev, 'snapshot'):
            return
        (ret, buff, err) = self.vcall([rcEnv.syspaths.zfs, 'destroy', snapdev ])
        if ret != 0:
            raise ex.syncSnapDestroyError
   0707010005ca0f000081b40000000000000000000000015ab217540002a6ae000000660000000000000000000000000000002200000000root/usr/share/opensvc/lib/svc.py """
The module defining the Svc class.
"""
from __future__ import print_function
from __future__ import unicode_literals

import sys
import os
import signal
import logging
import datetime
import time
import lock
import json
import re
import hashlib

from resources import Resource
from resourceset import ResourceSet
from freezer import Freezer
import rcStatus
from rcGlobalEnv import rcEnv, get_osvc_paths, Storage
from rcUtilities import justcall, lazy, unset_lazy, vcall, lcall, is_string, \
                        try_decode, action_triggers, read_cf, \
                        drop_option, fcache
from converters import *
import rcExceptions as ex
import rcLogger
import node
from rcScheduler import Scheduler, SchedOpts, sched_action
from comm import Crypt
from extconfig import ExtConfig

if sys.version_info[0] < 3:
    BrokenPipeError = IOError
else:
    raw_input = input


def signal_handler(*args):
    """
    A signal handler raising the excSignal exception.
    Args can be signum and frame, but we don't use them.
    """
    raise ex.excSignal

DEFAULT_WAITLOCK = 60

ACTION_NO_ASYNC = [
    "logs",
]

ACTION_ASYNC = {
    "abort": {
        "target": "aborted",
        "progress": "aborting",
    },
    "delete": {
        "target": "deleted",
        "progress": "deleting",
    },
    "freeze": {
        "target": "frozen",
        "progress": "freezing",
    },
    "giveback": {
        "target": "placed",
        "progress": "placing",
    },
    "provision": {
        "target": "provisioned",
        "progress": "provisioning",
    },
    "purge": {
        "target": "purged",
        "progress": "purging",
    },
    "shutdown": {
        "target": "shutdown",
        "progress": "shutting",
    },
    "start": {
        "target": "started",
        "progress": "starting",
    },
    "stop": {
        "target": "stopped",
        "progress": "stopping",
    },
    "toc": {
        "progress": "tocing",
    },
    "thaw": {
        "target": "thawed",
        "progress": "thawing",
    },
    "unprovision": {
        "target": "unprovisioned",
        "progress": "unprovisioning",
    },
}

TOP_STATUS_GROUPS = [
    "overall",
    "avail",
    "optional",
]

DEFAULT_STATUS_GROUPS = [
    "ip",
    "disk",
    "fs",
    "share",
    "container",
    "app",
    "sync",
    "task",
]

CONFIG_DEFAULTS = {
    'sync#i0_schedule': '@60',
    'sync_schedule': '04:00-06:00',
    'comp_schedule': '00:00-06:00',
    'status_schedule': '@9',
    'monitor_schedule': '@1',
    'resinfo_schedule': '@60',
    'no_schedule': '',
}

ACTIONS_NO_STATUS_CHANGE = [
    "abort",
    "clear",
    "docker",
    "frozen",
    "get",
    "giveback",
    "json_config",
    "json_status",
    "json_devs",
    "json_exposed_devs",
    "json_sub_devs",
    "json_base_devs",
    "logs",
    "print_config",
    "print_devs",
    "print_exposed_devs",
    "print_sub_devs",
    "print_base_devs",
    "print_config_mtime",
    "print_resource_status",
    "print_resinfo",
    "print_schedule",
    "print_status",
    "push_resinfo",
    "push_config",
    "push_encap_config",
    "prstatus",
    "resource_monitor",
    "status",
    "validate_config",
]

ACTIONS_ALLOW_ON_INVALID_NODE = [
    "abort",
    "clear",
    "delete",
    "edit_config",
    "frozen",
    "get",
    "logs",
    "print_config",
    "print_status",
    "set",
    "unset",
    "update",
    "validate_config",
]

ACTIONS_NO_LOG = [
    "delete",
    "edit_config",
    "get",
    "group_status",
    "logs",
    "push_resinfo",
    "push_config",
    "push_encap_config",
    "service_status",
    "resource_monitor",
    "set",
    "status",
    "unset",
    "validate_config",
]

ACTIONS_NO_TRIGGER = [
    "abort",
    "delete",
    "dns_update",
    "enable",
    "disable",
    "status",
    "pg_freeze",
    "pg_thaw",
    "pg_kill",
    "logs",
    "edit_config",
    "push_resinfo",
    "push_config",
    "push_encap_config",
    "group_status",
    "presync",
    "postsync",
    "resource_monitor",
    "set_provisioned",
    "set_unprovisioned",
    "status",
]

ACTIONS_LOCK_COMPAT = {
    "postsync": ["sync_all", "sync_nodes", "sync_drp", "sync_update", "sync_resync"],
}

ACTIONS_NO_LOCK = [
    "abort",
    "docker",
    "edit_config",
    "freeze",
    "frozen",
    "get",
    "logs",
    "push_resinfo",
    "push_config",
    "push_encap_config",
    "run",
    "status",
    "set_provisioned",
    "set_unprovisioned",
    "thaw",
    "toc",
    "validate_config",
]

START_GROUPS = [
    "ip",
    "sync.netapp",
    "sync.dcsckpt",
    "sync.nexenta",
    "sync.symclone",
    "sync.symsnap",
    "sync.symsrdfs",
    "sync.hp3par",
    "sync.ibmdssnap",
    "disk",
    "fs",
    "share",
    "container",
    "app",
]

STOP_GROUPS = [
    "app",
    "container",
    "share",
    "fs",
    "sync.btrfssnap",
    "disk",
    "ip",
]

STATUS_TYPES = [
    "app",
    "container.amazon",
    "container.docker",
    "container.esx",
    "container.hpvm",
    "container.jail",
    "container.kvm",
    "container.lxc",
    "container.lxd",
    "container.ldom",
    "container.openstack",
    "container.ovm",
    "container.srp",
    "container.vbox",
    "container.vcloud",
    "container.vz",
    "container.xen",
    "container.zone",
    "disk.drbd",
    "disk.gandi",
    "disk.gce",
    "disk.lock",
    "disk.loop",
    "disk.md",
    "disk.lv",
    "disk.raw",
    "disk.rados",
    "disk.scsireserv",
    "disk.vg",
    "disk.veritas",
    "disk.zpool",
    "fs",
    "fs.dir",
    "fs.docker",
    "ip",
    "ip.docker",
    "ip.cni",
    "share.nfs",
    "sync.btrfs",
    "sync.btrfssnap",
    "sync.dcsckpt",
    "sync.dcssnap",
    "sync.dds",
    "sync.docker",
    "sync.evasnap",
    "sync.hp3par",
    "sync.hp3parsnap",
    "sync.ibmdssnap",
    "sync.necismsnap",
    "sync.netapp",
    "sync.nexenta",
    "sync.radossnap",
    "sync.radosclone",
    "sync.rsync",
    "sync.symclone",
    "sync.symsnap",
    "sync.symsrdfs",
    "sync.s3",
    "sync.zfs",
    "sync.zfssnap",
    "task",
]

ACTIONS_DO_MASTER = [
    "freeze",
    "set_provisioned",
    "set_unprovisioned",
    "run",
    "thaw",
    "toc",
]

ACTIONS_DO_MASTER_AND_SLAVE = [
    "migrate",
    "prstart",
    "prstop",
    "restart",
    "shutdown",
    "start",
    "startstandby",
    "stop",
    "switch",
]

ACTIONS_NEED_SNAP_TRIGGER = [
    "sync_drp",
    "sync_nodes",
    "sync_resync",
    "sync_update",
]

TOPOLOGIES = [
    "failover",
    "flex",
]

os.environ['LANG'] = 'C'

def _slave_action(func):
    def need_specifier(self):
        """
        Raise an exception if --master or --slave(s) need to be set
        """
        if self.command_is_scoped():
            return
        if self.running_action in ACTIONS_DO_MASTER_AND_SLAVE + ACTIONS_DO_MASTER:
            return
        if self.options.master or self.options.slaves or self.options.slave is not None:
            return
        raise ex.excError("specify either --master, --slave(s) or both (%s)" % func.__name__)

    def _func(self):
        if self.encap or not self.has_encap_resources:
            return
        if self.command_is_scoped() and \
           len(set(self.action_rid) & set(self.encap_resources.keys())) == 0:
            self.log.info("skip action on slaves: no encap resources are selected")
            return
        need_specifier(self)
        if self.options.slaves or \
           self.options.slave is not None or \
           (not self.options.master and not self.options.slaves and self.options.slave is None and \
           self.running_action in ACTIONS_DO_MASTER_AND_SLAVE):
            try:
                func(self)
            except Exception as exc:
                raise ex.excError(str(exc))
    return _func

def _master_action(func):
    def need_specifier(self):
        """
        Raise an exception if --master or --slave(s) need to be set
        """
        if self.encap:
            return
        if not self.has_encap_resources:
            return
        if self.command_is_scoped():
            return
        if self.running_action in ACTIONS_DO_MASTER_AND_SLAVE + ACTIONS_DO_MASTER:
            return
        if self.options.master or self.options.slaves or self.options.slave is not None:
            return
        raise ex.excError("specify either --master, --slave(s) or both (%s)" % func.__name__)

    def _func(self):
        need_specifier(self)
        if self.options.master or \
           (not self.options.master and not self.options.slaves and self.options.slave is None and \
           self.running_action in ACTIONS_DO_MASTER_AND_SLAVE + ACTIONS_DO_MASTER):
            func(self)
    return _func

class Svc(Crypt, ExtConfig):
    """
    A OpenSVC service class.
    A service is a collection of resources.
    It exposes operations methods like provision, unprovision, stop, start,
    and sync.
    """
    # for ExtConfig
    default_status_groups = DEFAULT_STATUS_GROUPS

    def __init__(self, svcname=None, node=None, cf=None):
        self.type = "hosted"
        self.svcname = svcname
        self.node = node
        self.hostid = rcEnv.nodename
        self.paths = Storage(
            exe=os.path.join(rcEnv.paths.pathetc, self.svcname),
            cf=os.path.join(rcEnv.paths.pathetc, self.svcname+'.conf'),
            initd=os.path.join(rcEnv.paths.pathetc, self.svcname+'.d'),
            alt_initd=os.path.join(rcEnv.paths.pathetc, self.svcname+'.dir'),
            tmp_cf=os.path.join(rcEnv.paths.pathtmp, self.svcname+".conf.tmp")
        )
        if cf:
            self.paths.cf = cf
        self.resources_by_id = {}
        self.encap_resources = {}
        self.resourcesets_by_id = {}

        self.ref_cache = {}
        self.encap_json_status_cache = {}
        self.rset_status_cache = None
        self.lockfd = None
        self.abort_start_done = False
        self.action_start_date = datetime.datetime.now()
        self.has_encap_resources = False
        self.encap = False
        self.action_rid = []
        self.action_rid_before_depends = []
        self.action_rid_depends = []
        self.dependencies = {}
        self.running_action = None
        self.need_postsync = set()

        # set by the builder
        self.conf = os.path.join(rcEnv.paths.pathetc, svcname+".conf")
        self.comment = ""
        self.orchestrate = "ha"
        self.topology = "failover"
        self.placement = "nodes order"
        self.stonith = False
        self.parents = []
        self.show_disabled = False
        self.svc_env = rcEnv.node_env
        self.nodes = set([rcEnv.nodename])
        self.ordered_nodes = [rcEnv.nodename]
        self.drpnodes = set()
        self.ordered_drpnodes = []
        self.drpnode = ""
        self.encapnodes = set()
        self.flex_primary = ""
        self.drp_flex_primary = ""
        self.create_pg = False
        self.disable_rollback = False
        self.presync_done = False
        self.presnap_trigger = None
        self.postsnap_trigger = None
        self.monitor_action = None
        self.pre_monitor_action = None
        self.lock_timeout = DEFAULT_WAITLOCK

        # merged by the cmdline parser
        self.options = Storage(
            color="auto",
            local=False,
            slaves=False,
            slave=None,
            master=False,
            cron=False,
            follow=False,
            force=False,
            remote=False,
            debug=False,
            disable_rollback=False,
            show_disabled=None,
            moduleset="",
            module="",
            ruleset_date="",
            dry_run=False,
            refresh=False,
            rid=None,
            tags=None,
            subsets=None,
            discard=False,
            recover=False,
            waitlock=DEFAULT_WAITLOCK,
            wait=False,
        )

    @lazy
    def kwdict(self):
        return __import__("svcdict")

    @lazy
    def var_d(self):
        var_d = os.path.join(rcEnv.paths.pathvar, "services", self.svcname)
        if not os.path.exists(var_d):
            os.makedirs(var_d, 0o755)
        return var_d

    @lazy
    def log(self):
        return rcLogger.initLogger(rcEnv.nodename+"."+self.svcname)

    @lazy
    def sched(self):
        """
        Lazy init of the service scheduler.
        """
        return Scheduler(
            name=self.svcname,
            config_defaults=CONFIG_DEFAULTS,
            options=self.options,
            config=self.config,
            log=self.log,
            svc=self,
            scheduler_actions={
                "compliance_auto": SchedOpts(
                    "DEFAULT",
                    fname="last_comp_check",
                    schedule_option="comp_schedule"
                ),
                "push_resinfo": SchedOpts(
                    "DEFAULT",
                    fname="last_push_resinfo",
                    schedule_option="resinfo_schedule"
                ),
            },
        )

    @lazy
    def ha(self):
        if self.topology == "flex":
            return True
        if self.has_monitored_resources():
            return True
        if self.orchestrate == "ha":
            return True
        return False

    @lazy
    def peers(self):
        if rcEnv.nodename in self.nodes:
            return self.nodes
        elif rcEnv.nodename in self.drpnodes:
            return self.drpnodes
        else:
            return []

    @lazy
    def ordered_peers(self):
        if rcEnv.nodename in self.nodes:
            return self.ordered_nodes
        elif rcEnv.nodename in self.drpnodes:
            return self.ordered_drpnodes
        else:
            return []

    @lazy
    def dockerlib(self):
        """
        Lazy allocator for the dockerlib object.
        """
        import rcDocker
        return rcDocker.DockerLib(self)

    @lazy
    def freezer(self):
        """
        Lazy allocator for the freezer object.
        """
        return Freezer(self.svcname)

    @lazy
    def compliance(self):
        from compliance import Compliance
        comp = Compliance(self)
        return comp

    @lazy
    def disabled(self):
        try:
            return self.conf_get("DEFAULT", "disable")
        except ex.OptNotFound as exc:
            return exc.default

    @lazy
    def children(self):
        try:
            children = self.conf_get('DEFAULT', "children")
        except ex.OptNotFound as exc:
            children = exc.default
        return children

    @lazy
    def slaves(self):
        try:
            slaves = self.conf_get('DEFAULT', "slaves")
        except ex.OptNotFound as exc:
            slaves = exc.default
        return slaves

    @lazy
    def children_and_slaves(self):
        data = self.children + self.slaves
        if self.scaler is not None:
            data += self.scaler.slaves
        return data

    @lazy
    def scaler_slave(self):
        try:
            scaler_slave = self.conf_get('DEFAULT', "scaler_slave")
        except ex.OptNotFound as exc:
            scaler_slave = exc.default
        return scaler_slave

    @lazy
    def scale_target(self):
        try:
            val = self.conf_get("DEFAULT", "scale")
            if isinstance(val, int) and val < 0:
                val = 0
        except ex.OptNotFound as exc:
            val = exc.default
        return val

    @lazy
    def scaler(self):
        if self.scale_target is None:
            return
        data = Storage({
            "slaves": [],
        })
        if self.topology == "flex":
            data.width = len(self.peers)
            if data.width == 0:
                data.left = 0
            else:
                data.left = self.scale_target % data.width
            if self.scale_target == 0:
                data.slaves_count = 0
            elif data.left == 0:
                data.slaves_count = self.scale_target // data.width
            else:
                data.slaves_count = (self.scale_target // data.width) + 1
        else:
            data.width = 1
            data.left = 0
            data.slaves_count = self.scale_target

        for idx in range(data.slaves_count):
            name = str(idx) + "." + self.svcname
            if name not in data.slaves:
                data.slaves.append(name)
        return data

    @lazy
    def constraints(self):
        """
        Return True if no constraints is defined or if defined constraints are
        met. Otherwise return False.
        """
        try:
            return convert_boolean(self.conf_get("DEFAULT", "constraints"))
        except ex.OptNotFound:
            return True
        except ex.excError:
            return True

    @lazy
    def hard_affinity(self):
        try:
            return self.conf_get("DEFAULT", "hard_affinity")
        except ex.OptNotFound as exc:
            return exc.default

    @lazy
    def hard_anti_affinity(self):
        try:
            return self.conf_get("DEFAULT", "hard_anti_affinity")
        except ex.OptNotFound as exc:
            return exc.default

    @lazy
    def soft_affinity(self):
        try:
            return self.conf_get("DEFAULT", "soft_affinity")
        except ex.OptNotFound as exc:
            return exc.default

    @lazy
    def soft_anti_affinity(self):
        try:
            return self.conf_get("DEFAULT", "soft_anti_affinity")
        except ex.OptNotFound as exc:
            return exc.default

    @lazy
    def flex_min_nodes(self):
        try:
           val = self.conf_get('DEFAULT', 'flex_min_nodes')
        except ex.OptNotFound as exc:
           return exc.default
        if val < 0:
           val = 0
        nb_nodes = len(self.nodes|self.drpnodes)
        if val > nb_nodes:
           val = nb_nodes
        return val

    @lazy
    def flex_max_nodes(self):
        nb_nodes = len(self.peers)
        try:
           val = self.conf_get('DEFAULT', 'flex_max_nodes')
        except ex.OptNotFound:
           return nb_nodes
        if val > nb_nodes:
           val = nb_nodes
        if val < self.flex_min_nodes:
           val = self.flex_min_nodes
        return val

    @lazy
    def flex_cpu_low_threshold(self):
        try:
            val = self.conf_get('DEFAULT', 'flex_cpu_low_threshold')
        except ex.OptNotFound as exc:
            return exc.default
        if val < 0:
            return 0
        if val > 100:
            return 100
        return val

    @lazy
    def flex_cpu_high_threshold(self):
        try:
            val = self.conf_get('DEFAULT', 'flex_cpu_high_threshold')
        except ex.OptNotFound as exc:
            return exc.default
        if val < self.flex_cpu_low_threshold:
            return self.flex_cpu_low_threshold
        if val > 100:
            return 100
        return val

    @lazy
    def app(self):
        """
        Return the service app code.
        """
        try:
            return self.conf_get("DEFAULT", "app")
        except ex.OptNotFound as exc:
            return exc.default

    def get_node(self):
        if self.node is None:
            self.node = node.Node()
        return self.node

    def __lt__(self, other):
        """
        Order by service name
        """
        return self.svcname < other.svcname

    def register_dependency(self, action, _from, _to):
        if action not in self.dependencies:
            self.dependencies[action] = {}
        if _from not in self.dependencies[action]:
            self.dependencies[action][_from] = set()
        self.dependencies[action][_from].add(_to)

    @lazy
    def shared_resources(self):
        return [res for res in self.get_resources() if res.shared]

    def action_rid_dependencies(self, action, rid):
        if action in ("provision", "start"):
            action = "start"
        elif action in ("shutdown", "unprovision", "stop"):
            action = "stop"
        else:
            return set()
        if action not in self.dependencies:
            return set()
        if rid not in self.dependencies[action]:
            return set()
        return self.dependencies[action][rid]

    def action_rid_dependency_of(self, action, rid):
        if action in ("provision", "start"):
            action = "start"
        elif action in ("shutdown", "unprovision", "stop"):
            action = "stop"
        else:
            return set()
        if action not in self.dependencies:
            return set()
        dependency_of = set()
        for _rid, dependencies in self.dependencies[action].items():
            if rid in dependencies:
                dependency_of.add(_rid)
        return dependency_of

    def resource_handling_dir(self, path):
        mntpts = {}
        for res in self.get_resources(["fs"]):
            mntpts[res.mount_point] = res
        while True:
            if path in mntpts.keys():
                return mntpts[path]
            path = os.path.dirname(path)
            if path == os.sep:
                return

    def print_schedule(self):
        """
        The 'print schedule' node and service action entrypoint.
        """
        return self.sched.print_schedule()

    def has_monitored_resources(self):
        for res in self.get_resources():
            if res.monitor and not res.is_disabled():
                return True
        return False

    def post_build(self):
        """
        A method run after the service is done building.
        Add resource-dependent tasks to the scheduler.
        """
        try:
            monitor_schedule = self.conf_get('DEFAULT', 'monitor_schedule')
        except ex.OptNotFound:
            monitor_schedule = None

        if not self.encap:
            self.sched.scheduler_actions["status"] = SchedOpts(
                    "DEFAULT",
                    fname="last_status",
                    schedule_option="status_schedule"
            )
            if self.has_monitored_resources() or monitor_schedule is not None:
                self.sched.scheduler_actions["resource_monitor"] = SchedOpts(
                    "DEFAULT",
                    fname="last_resource_monitor",
                    schedule_option="monitor_schedule"
                )

        syncs = []
        for resource in self.get_resources("sync"):
            schedule_option = "sync_schedule" if resource.rid != "sync#i0" \
                              else "sync#i0_schedule"
            syncs += [SchedOpts(
                resource.rid,
                fname="last_syncall_"+resource.rid,
                schedule_option=schedule_option
            )]
        if len(syncs) > 0:
            self.sched.scheduler_actions["sync_all"] = syncs

        tasks = []
        for resource in self.get_resources("task"):
            tasks += [SchedOpts(
                resource.rid,
                fname="last_"+resource.rid,
                schedule_option="no_schedule"
            )]
        if len(tasks) > 0:
            self.sched.scheduler_actions["run"] = tasks


    def purge_status_caches(self):
        """
        Purge the json cache and each resource status on-disk cache.
        """
        self.purge_status_last()
        self.purge_status_data_dump()

    def purge_status_data_dump(self):
        """
        Purge the json status dump
        """
        if os.path.exists(self.status_data_dump):
            os.unlink(self.status_data_dump)

    def purge_status_last(self):
        """
        Purge all service resources on-disk status caches.
        """
        for rset in self.resourcesets_by_id.values():
            rset.purge_status_last()

    def get_subset_parallel(self, rtype):
        """
        Return True if the resources of a resourceset can run an action in
        parallel executing per-resource workers.
        """
        rtype = rtype.split(".")[0]
        subset_section = 'subset#' + rtype
        if not self.config.has_section(subset_section):
            return False
        try:
            return self.conf_get(subset_section, "parallel")
        except ex.OptNotFound:
            return False

    def get_scsireserv(self, rid):
        """
        Get the 'scsireserv' config keyword value for rid.
        """
        try:
            return self.conf_get(rid, 'scsireserv')
        except ex.OptNotFound as exc:
            return exc.default

    def add_scsireserv(self, resource):
        """
        Add a 'pr' suffixed co-resource.
        """
        try:
            if not self.get_scsireserv(resource.rid):
                # scsireserv not enabled on this resource
                return
        except Exception:
            # scsireserv not supported on this resource
            return

        try:
            sr = __import__('resScsiReserv'+rcEnv.sysname)
        except ImportError:
            sr = __import__('resScsiReserv')

        kwargs = {}
        pr_rid = resource.rid+"pr"

        try:
            kwargs["prkey"] = self.conf_get(resource.rid, 'prkey')
        except ex.OptNotFound as exc:
            kwargs["prkey"] = exc.default

        try:
            kwargs['no_preempt_abort'] = self.conf_get(resource.rid, 'no_preempt_abort')
        except ex.OptNotFound as exc:
            kwargs['no_preempt_abort'] = exc.default

        try:
            kwargs['optional'] = self.conf_get(pr_rid, "optional")
        except ex.OptNotFound:
            kwargs['optional'] = resource.is_optional()

        try:
            kwargs['disabled'] = self.conf_get(pr_rid, "disable")
        except ex.OptNotFound:
            kwargs['disabled'] = resource.is_disabled()

        try:
            kwargs['restart'] = self.conf_get(pr_rid, "restart")
        except ex.OptNotFound:
            kwargs['restart'] = resource.restart if hasattr(resource, "restart") else False

        try:
            kwargs['monitor'] = self.conf_get(pr_rid, "monitor")
        except ex.OptNotFound:
            kwargs['monitor'] = resource.monitor

        try:
            kwargs['tags'] = self.conf_get(pr_rid, "tags")
        except ex.OptNotFound:
            kwargs['tags'] = resource.tags

        try:
            kwargs['subset'] = self.conf_get(pr_rid, "subset")
        except ex.OptNotFound:
            kwargs['subset'] = resource.subset

        try:
            kwargs['shared'] = self.conf_get(pr_rid, "shared")
        except ex.OptNotFound:
            kwargs['shared'] = resource.shared

        try:
            kwargs['standby'] = self.conf_get(pr_rid, "standby")
        except ex.OptNotFound:
            kwargs['standby'] = resource.standby

        kwargs['rid'] = resource.rid
        kwargs['peer_resource'] = resource

        r = sr.ScsiReserv(**kwargs)
        self += r

    def add_requires(self, resource):
        actions = [
          'unprovision', 'provision',
          'stop', 'start',
        ]
        if resource.type == "sync":
            actions += [
                'sync_nodes', 'sync_drp',
                'sync_resync', 'sync_break',
                'sync_update',
            ]
        if resource.type == "task":
            actions += [
                'run',
            ]
        for action in actions:
            try:
                s = self.conf_get(resource.rid, action+'_requires')
            except ex.OptNotFound:
                continue
            s = s.replace("stdby ", "stdby_")
            l = s.split(" ")
            l = list(map(lambda x: x.replace("stdby_", "stdby "), l))
            setattr(resource, action+'_requires', l)

    def __iadd__(self, other):
        """
        Svc += ResourceSet
        Svc += Resource
        """
        if hasattr(other, 'resources'):
            if other.rid in self.resourcesets_by_id:
                self.resourcesets_by_id[other.rid] += other
            else:
                # new ResourceSet or ResourceSet-derived class
                self.resourcesets_by_id[other.rid] = other
                other.svc = self
            return self

        base_type = other.type.split(".")[0]
        if other.subset is not None:
            # the resource wants to be added to a specific resourceset
            # for action grouping, parallel execution or sub-resource
            # triggers
            rset_id = "%s:%s" % (base_type, other.subset)
        else:
            rset_id = base_type

        if rset_id in self.resourcesets_by_id:
            # the resource set already exists. add resource or resourceset.
            self.resourcesets_by_id[rset_id] += other
        elif isinstance(other, Resource):
            parallel = self.get_subset_parallel(rset_id)
            rset = ResourceSet(rset_id, resources=[other], parallel=parallel)
            rset.svc = self
            rset.pg_settings = self.get_pg_settings("subset#"+rset_id)
            self.__iadd__(rset)
        else:
            self.log.debug("unexpected object addition to the service: %s",
                           str(other))

        other.svc = self

        if isinstance(other, Resource) and other.rid and "#" in other.rid:
            other.pg_settings = self.get_pg_settings(other.rid)
            self.add_scsireserv(other)
            self.add_requires(other)
            self.resources_by_id[other.rid] = other

        if not other.is_disabled() and hasattr(other, "on_add"):
            other.on_add()

        return self

    def dblogger(self, action, begin, end, actionlogfile):
        """
        Send to the collector the service status after an action, and
        the action log.
        """
        self.node.daemon_collector_xmlrpc('end_action', self.svcname, action,
                                          begin, end, self.options.cron,
                                          actionlogfile)
        try:
            logging.shutdown()
        except:
            pass

    def validate_mon_action(self, action):
        if action in ("freeze", "abort"):
            return
        if self.options.local:
            return
        data = self.get_smon_data()
        if data is None:
            return
        for nodename, _data in data["instances"].items():
            status = _data.get("status", "unknown")
            if status != "idle":
                raise ex.excError("instance on node %s in %s state"
                                  "" % (nodename, status))
        global_expect = data.get("service", {}).get("global_expect")
        if global_expect is not None:
            raise ex.excError("service has already been asked to reach the "
                              "%s global state" % global_expect)

        data = self.node._daemon_status()
        if self.svcname not in data["monitor"]["services"]:
            return
        if action in ("start", "stop"):
            avail = data["monitor"]["services"][self.svcname]["avail"]
            if action == "start" and avail == "up":
                raise ex.excError("the service is already started.")
            elif action == "stop" and avail in ("down", "stdby down"):
                raise ex.excError("the service is already stopped.")
            if avail in ("n/a", "undef"):
                raise ex.excError("the service is in '%s' aggregated avail "
                                  "status. the daemons won't honor this "
                                  "request, so don't submit it." % avail)
            avails = set([data["monitor"]["nodes"][node]["services"]["status"].get(self.svcname, {}).get("avail") for node in data["monitor"]["nodes"]])
            if action == "start" and len(avails & set(["warn"])) > 0:
                raise ex.excError("the service has instances in 'warn' avail "
                                  "status. the daemons won't honor this request,"
                                  " so don't submit it.")

    def started_on(self):
        nodenames = []
        data = self.get_smon_data()
        if data is None:
            return []
        if "instances" not in data:
            return []
        for nodename, _data in data["instances"].items():
            status = _data.get("local_expect")
            if status == "started":
                nodenames.append(nodename)
        return nodenames

    def svclock(self, action=None, timeout=30, delay=1):
        """
        Acquire the service action lock.
        """
        suffix = None
        if (action not in ACTION_NO_ASYNC and self.options.node is not None and self.options.node != "") or \
           action in ACTIONS_NO_LOCK or \
           action.startswith("collector") or \
           self.lockfd is not None:
            # explicitly blacklisted or
            # no need to serialize requests or
            # already acquired
            return

        if action.startswith("compliance"):
            # compliance modules are allowed to execute actions on the service
            # so give them their own lock
            suffix = "compliance"
        elif action.startswith("sync"):
            suffix = "sync"

        lockfile = os.path.join(rcEnv.paths.pathlock, self.svcname)
        if suffix is not None:
            lockfile = ".".join((lockfile, suffix))

        details = "(timeout %d, delay %d, action %s, lockfile %s)" % \
                  (timeout, delay, action, lockfile)
        self.log.debug("acquire service lock %s", details)

        # try an immmediate lock acquire and see if the running action is
        # compatible
        if action in ACTIONS_LOCK_COMPAT:
            try:
                lockfd = lock.lock(
                    timeout=0,
                    delay=delay,
                    lockfile=lockfile,
                    intent=action
                )
                if lockfd is not None:
                    self.lockfd = lockfd
                return
            except lock.LockTimeout as exc:
                if exc.intent in ACTIONS_LOCK_COMPAT[action]:
                    return
                # not compatible, continue with the normal acquire
            except Exception:
                pass

        try:
            lockfd = lock.lock(
                timeout=timeout,
                delay=delay,
                lockfile=lockfile,
                intent=action
            )
        except lock.LockTimeout as exc:
            raise ex.excError("timed out waiting for lock %s: %s" % (details, str(exc)))
        except lock.LockNoLockFile:
            raise ex.excError("lock_nowait: set the 'lockfile' param %s" % details)
        except lock.LockCreateError:
            raise ex.excError("can not create lock file %s" % details)
        except lock.LockAcquire as exc:
            raise ex.excError("another action is currently running %s: %s" % (details, str(exc)))
        except ex.excSignal:
            raise ex.excError("interrupted by signal %s" % details)
        except Exception as exc:
            self.save_exc()
            raise ex.excError("unexpected locking error %s: %s" % (details, str(exc)))

        if lockfd is not None:
            self.lockfd = lockfd

    def svcunlock(self):
        """
        Release the service action lock.
        """
        lock.unlock(self.lockfd)
        self.lockfd = None

    @staticmethod
    def setup_signal_handlers():
        """
        Install signal handlers.
        """
        signal.signal(signal.SIGINT, signal_handler)
        signal.signal(signal.SIGTERM, signal_handler)

    def get_resource(self, rid):
        """
        Return a resource object by id.
        Return None if the rid is not found.
        """
        if rid not in self.resources_by_id:
            return
        return self.resources_by_id[rid]

    def get_resources(self, _type=None, discard_disabled=True):
        """
        Return the list of resources matching criteria.

        <_type> can be:
          None: all resources are returned
        """
        if _type is None:
            return self.resources_by_id.values()
        if not isinstance(_type, (list, tuple)):
            _types = [_type]
        else:
            _types = _type

        resources = []
        for resource in self.resources_by_id.values():
            if not self.encap and resource.encap:
                continue
            if discard_disabled and resource.is_disabled():
                continue
            for t in _types:
                if "." in t and resource.type == t or \
                   "." not in t and t == resource.type.split(".")[0]:
                    resources.append(resource)
        return resources

    def get_resourcesets(self, _type, strict=False):
        """
        Return the list of resourceset matching the specified types.
        """
        if not isinstance(_type, (set, list, tuple)):
            _types = [_type]
        else:
            _types = _type
        rsets = {}
        for _type in _types:
            rsets[_type] = {}
        for _type in _types:
            for rset in self.resourcesets_by_id.values():
                if rset.has_resource_with_types([_type], strict=strict):
                    rsets[_type][rset.rid] = rset
        _rsets = []
        for _type in _types:
            for rset_id, rset in sorted(rsets[_type].items()):
                if rset not in _rsets:
                    _rsets.append(rset)
        return _rsets

    def all_set_action(self, action=None, tags=None):
        """
        Execute an action on all resources all resource sets.
        """
        self.set_action(self.resourcesets_by_id.values(), action=action, tags=tags)

    def sub_set_action(self, _type=None, action=None, tags=None, xtags=None,
                       strict=False):
        """
        Execute an action on all resources of the resource sets of the
        specified type.
        """
        if not isinstance(_type, (list, tuple, set)):
            _type = [_type]
        rsets = []
        for __type in _type:
            _rsets = []
            for rset in self.get_resourcesets(__type, strict=strict):
                if rset not in rsets and rset not in _rsets:
                    _rsets.append(rset)

            if action in ["start", "startstandby", "provision"] or \
                self.type.startswith("sync"):
                _rsets.sort()
            else:
                _rsets.sort(reverse=True)
            rsets += _rsets
        self.set_action(rsets, _type=_type, action=action, tags=tags, xtags=xtags)

    def need_snap_trigger(self, rsets, action):
        """
        Return True if the action is a sync action and at least one of the
        specified resource sets has a resource requiring a snapshot.
        """
        if action not in ACTIONS_NEED_SNAP_TRIGGER:
            return False
        for rset in rsets:
            for resource in rset.resources:
                # avoid to run pre/post snap triggers when there is no
                # resource flagged for snap and on drpnodes
                if hasattr(resource, "snap") and resource.snap is True and \
                   rcEnv.nodename in self.nodes:
                    return True
        return False

    def action_triggers(self, trigger, action, **kwargs):
        """
        Executes a resource trigger. Guess if the shell mode is needed from
        the trigger syntax.
        """
        action_triggers(self, trigger, action, **kwargs)

    def set_action(self, rsets=None, _type=None, action=None, tags=None, xtags=None):
        """
        Call the action on all sets sorted resources.
        If the sets define a pre_snap trigger run that before the action.
        If the sets define a pre_<action> trigger run that before the action.
        If the sets define a post_<action> trigger run that after the action.
        """
        if rsets is None:
            rsets = []
        if tags is None:
            tags = set()
        if xtags is None:
            xtags = set()

        def do_trigger(when):
            """
            Excecute a <when> trigger on each resource of the set,
            if the action allows triggers.
            If a trigger raises,
            * excError, stop looping over the resources and propagate up
              to the caller.
            * any other exception, save the traceback in the debug log
              and stop looping over the resources and raise an excError
            """
            aborted = []
            for rset in rsets:
                if action in ACTIONS_NO_TRIGGER or rset.all_skip(action):
                    break
                try:
                    rset.log.debug("start %s %s_action", rset.rid, when)
                    aborted += getattr(rset, when + "_action")(action, types=_type, tags=tags, xtags=xtags)
                except ex.excError:
                    raise
                except:
                    self.save_exc()
                    raise ex.excError
            return aborted

        def do_snap_trigger(when):
            """
            Execute the <when>snap trigger.
            """
            if not need_snap:
                return
            trigger = getattr(self, when + "snap_trigger")
            if trigger is None:
                return
            results = self.vcall(trigger)
            if results[0] != 0:
                raise ex.excError(results[2])

        need_snap = self.need_snap_trigger(rsets, action)

        # snapshots are created in pre_action and destroyed in post_action
        # place presnap and postsnap triggers around pre_action
        do_snap_trigger("pre")
        aborted = do_trigger("pre")
        do_snap_trigger("post")

        last = None
        for rset in rsets:
            # upto / downto break
            current = rset.rid.split(":")[0]
            if last and current != last and (self.options.upto == last or self.options.downto == last):
                if self.options.upto:
                    barrier = "up to %s" % self.options.upto
                else:
                    barrier = "down to %s" % self.options.downto
                self.log.info("reached '%s' barrier" % barrier)
                break
            last = current
            self.log.debug('set_action: action=%s rset=%s', action, rset.rid)
            rset.action(action, types=_type, tags=tags, xtags=xtags, xtypes=aborted)

        do_trigger("post")

    def __str__(self):
        """
        The Svc class print formatter.
        """
        output = self.svcname
        for rset in self.resourcesets_by_id.values():
            output += "  [%s]" % str(rset)
        return output

    def prstatus(self):
        status = rcStatus.Status()
        for resource in self.get_resources("disk.scsireserv"):
            status += resource.status()
        return int(status)

    def status(self):
        """
        Return the aggregate status a service.
        """
        refresh = self.options.refresh or (not self.encap and self.options.cron)
        data = self.print_status_data(mon_data=False, refresh=refresh)
        return rcStatus.Status(data["overall"]).value()

    @fcache
    def get_mon_data(self):
        data = self.node._daemon_status(silent=True)
        if data is not None:
            return data["monitor"]
        return {}

    def get_smon_data(self):
        data = {}
        try:
            mon_data = self.get_mon_data()
            data["compat"] = mon_data["compat"]
            data["service"] = mon_data["services"][self.svcname]
            data["instances"] = {}
            for nodename in mon_data["nodes"]:
                 try:
                     data["instances"][nodename] = mon_data["nodes"][nodename]["services"]["status"][self.svcname]["monitor"]
                 except KeyError:
                     pass
            return data
        except Exception:
            return

    @lazy
    def status_data_dump(self):
        return os.path.join(self.var_d, "status.json")

    def status_data_dump_outdated(self):
        """
        Return True if the status data dump is older than the last config file
        modification time.
        """
        try:
            return os.stat(self.paths.cf).st_mtime > os.stat(self.status_data_dump).st_mtime
        except OSError as exc:
            return True

    def print_status_data(self, from_resource_status_cache=False, mon_data=False, refresh=False):
        """
        Return a structure containing hierarchical status of
        the service and monitor information. Fetch CRM status from cache if
        possible and allowed by kwargs.
        """
        lockfile = os.path.join(rcEnv.paths.pathlock, self.svcname + ".status")
        try:
            with lock.cmlock(timeout=30, delay=1, lockfile=lockfile):
                if not from_resource_status_cache and \
                   not refresh and \
                   os.path.exists(self.status_data_dump) and \
                   not self.status_data_dump_outdated():
                    try:
                        with open(self.status_data_dump, 'r') as filep:
                            data = json.load(filep)
                    except ValueError:
                        data = self.print_status_data_eval(refresh=refresh)
                else:
                    data = self.print_status_data_eval(refresh=refresh)
        except lock.LOCK_EXCEPTIONS as exc:
            raise ex.excAbortAction(str(exc))

        if mon_data:
            mon_data = self.get_smon_data()
            try:
                data["cluster"] = {
                    "compat": mon_data["compat"],
                    "avail": mon_data["service"]["avail"],
                    "overall": mon_data["service"]["overall"],
                    "placement": mon_data["service"]["placement"],
                }
                data["monitor"] = mon_data["instances"][rcEnv.nodename]
            except:
                pass

        if not refresh:
            data["running"] = self.get_running()
        return data

    def get_running(self):
        lockfile = os.path.join(rcEnv.paths.pathlock, self.svcname)
        running = []
        running += [self._get_running(lockfile).get("rid")]
        lockfile = os.path.join(rcEnv.paths.pathlock, self.svcname+".sync")
        running += [self._get_running(lockfile).get("rid")]
        for task in self.get_resources("task"):
            lockfile = os.path.join(task.var_d, "run.lock")
            running += [self._get_running(lockfile).get("rid")]
        return [rid for rid in running if rid]

    def _get_running(self, lockfile):
        try:
            with lock.cmlock(lockfile=lockfile, timeout=0):
                return {}
        except Exception as exc:
            # failing to open "w+", action in progress
            try:
                with open(lockfile, "r") as ofile:
                    #ofile.seek(0)
                    lock_data = json.load(ofile)
                    return lock_data.get("progress", {})
            except Exception:
                pass
            return {}
        return {}

    def print_status_data_eval(self, refresh=False):
        """
        Return a structure containing hierarchical status of
        the service.
        """
        now = time.time()
        group_status = self.group_status(refresh=refresh)

        data = {
            "updated": datetime.datetime.utcfromtimestamp(now).strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
            "mtime": now,
            "app": self.app,
            "placement": self.placement,
            "topology": self.topology,
            "provisioned": True,
            "resources": {},
        }
        data["running"] = self.get_running()
        if self.topology == "flex":
            data.update({
                "flex_min_nodes": self.flex_min_nodes,
                "flex_max_nodes": self.flex_max_nodes,
            })
        frozen = self.frozen()
        if frozen:
            data["frozen"] = frozen
        if not self.constraints:
            data["constraints"] = self.constraints
        if self.slaves:
            data["slaves"] = self.slaves
        if len(self.parents) > 0:
            data["parents"] = self.parents
        if len(self.children) > 0:
            data["children"] = self.children
        if self.orchestrate != "no":
            data["orchestrate"] = self.orchestrate
        if self.scale_target is not None:
            data["scale"] = self.scale_target
        if self.scaler_slave:
            data["scaler_slave"] = self.scaler_slave
        if self.scaler is not None:
            data["scaler_slaves"] = self.scaler.slaves

        containers = self.get_resources('container')
        if len(containers) > 0:
            data['encap'] = {}
            for container in containers:
                if not self.has_encap_resources:
                    continue
                if container.name is None or len(container.name) == 0:
                    # docker case
                    continue
                try:
                    data['encap'][container.rid] = self.encap_json_status(container, refresh=refresh)
                    # merge container overall status, so we propagate encap alerts
                    # up to instance and service level.
                    group_status["overall"] += rcStatus.Status(data["encap"][container.rid]["overall"] if "overall" in data["encap"][container.rid] else "n/a")
                    group_status["avail"] += rcStatus.Status(data["encap"][container.rid]["avail"] if "avail" in data["encap"][container.rid] else "n/a")
                except:
                    data["encap"][container.rid] = {"resources": {}}
                if hasattr(container, "vm_hostname"):
                    data["encap"][container.rid]["hostname"] = container.vm_hostname
        elif self.encap:
            data['encap'] = True

        for rset in self.get_resourcesets(STATUS_TYPES, strict=True):
            for resource in rset.resources:
                status = rcStatus.Status(resource.status(verbose=True))
                log = resource.status_logs_strlist()
                info = resource.status_info()
                tags = sorted(list(resource.tags))
                disable = resource.is_disabled()
                _data = {
                    "status": str(status),
                    "type": resource.type,
                    "label": resource.label,
                }
                _data["provisioned"] = resource.provisioned_data()
                if disable:
                    _data["disable"] = disable
                if resource.standby:
                    _data["standby"] = resource.standby
                if resource.encap:
                    _data["encap"] = resource.encap
                if resource.optional:
                    _data["optional"] = resource.optional
                if resource.monitor:
                    _data["monitor"] = resource.monitor
                if len(log) > 0:
                    _data["log"] = log
                if len(info) > 0:
                    _data["info"] = info
                if len(tags) > 0:
                    _data["tags"] = tags
                if _data["provisioned"]["state"] is False:
                    data["provisioned"] = False
                if resource.subset:
                    _data["subset"] = resource.subset
                data["resources"][resource.rid] = _data
        for group in TOP_STATUS_GROUPS:
            group_status[group] = str(group_status[group])
        for group in group_status["status_group"]:
            group_status["status_group"][group] = str(group_status["status_group"][group])
        data.update(group_status)
        if self.stonith and self.topology == "failover" and data["avail"] == "up":
            data["stonith"] = True
        self.write_status_data(data)
        return data

    def csum_status_data(self, data):
        h = hashlib.md5()
        def fn(h, val):
            if type(val) == dict:
                for key, _val in val.items():
                    if key in ("status_updated", "updated", "mtime"):
                        continue
                    h = fn(h, _val)
            elif type(val) == list:
                for _val in val:
                    h = fn(h, _val)
            else:
                h.update(repr(val).encode())
            return h
        return fn(h, data).hexdigest()

    def write_status_data(self, data):
        data["csum"] = self.csum_status_data(data)
        try:
            with open(self.status_data_dump, "w") as filep:
                json.dump(data, filep)
                filep.flush()
            os.utime(self.status_data_dump, (-1, data["mtime"]))
            self.wake_monitor()
        except Exception as exc:
            self.log.warning("failed to update %s: %s",
                             self.status_data_dump, str(exc))
        return data

    def update_status_data(self):
        if self.options.minimal:
            return
        self.log.debug("update status dump")
        data = self.print_status_data(from_resource_status_cache=True)
        self.write_status_data(data)

    def env_section_keys_evaluated(self):
        """
        Return the dict of key/val pairs in the [env] section of the
        service configuration, after dereferencing.
        """
        return self.env_section_keys(evaluate=True)

    def env_section_keys(self, evaluate=False):
        """
        Return the dict of key/val pairs in the [env] section of the
        service configuration, without dereferencing.
        """
        config = self.print_config_data()
        try:
            from collections import OrderedDict
            data = OrderedDict()
        except ImportError:
            data = {}
        for key in config.get("env", {}).keys():
            key = key.split("@")[0]
            if key in data:
                continue
            if evaluate:
                data[key] = self.conf_get('env', key)
            else:
                data[key] = config["env"][key]
        return data

    def print_resource_status(self):
        """
        Print a single resource status string.
        """
        if len(self.action_rid) != 1:
            print("action 'print_resource_status' is not allowed on mutiple "
                  "resources", file=sys.stderr)
            return 1
        for rid in self.action_rid:
            if rid not in self.resources_by_id:
                print("resource not found")
                continue
            resource = self.resources_by_id[rid]
            print(rcStatus.colorize_status(str(resource.status(refresh=self.options.refresh))))
        return 0

    def print_status(self):
        """
        Display in human-readable format the hierarchical service status.
        """
        data = self.print_status_data(mon_data=True, refresh=self.options.refresh)
        if self.options.format is not None:
            return data

        from rcColor import color, colorize, STATUS_COLOR
        from forest import Forest

        def fmt_flags(resource, running):
            """
            Format resource flags as a vector of character.

            R  Running
            M  Monitored
            D  Disabled
            O  Optional
            E  Encap
            P  Provisioned
            S  Standby
            """
            flags = ''
            flags += "R" if resource["rid"] in running else '.'
            flags += 'M' if resource.get("monitor") else '.'
            flags += 'D' if resource.get("disable") else '.'
            flags += 'O' if resource.get("optional") else '.'
            flags += 'E' if resource.get("encap") else '.'
            provisioned = resource.get("provisioned", {}).get("state")
            if provisioned is True:
                flags += '.'
            elif provisioned is False:
                flags += 'P'
            else:
                flags += '/'
            flags += 'S' if resource.get("standby") else '.'
            return flags

        def dispatch_resources(data):
            """
            Sorted resources.
            Honor the --discard-disabled arg.
            """
            subsets = {}
            for group in DEFAULT_STATUS_GROUPS:
                subsets[group] = {}

            for rid, resource in data["resources"].items():
                if discard_disabled and resource["disable"]:
                    continue
                group = resource["type"].split(".", 1)[0]
                if "subset" in resource:
                    subset = group + ":" + resource["subset"]
                else:
                    subset = group
                if subset not in subsets[group]:
                    subsets[group][subset] = []
                resource["rid"] = rid
                subsets[group][subset].append(resource)

            return subsets

        def add_subsets(subsets, node_nodename, running):
            for group in DEFAULT_STATUS_GROUPS:
                subset_names = sorted(subsets[group])
                for subset in subset_names:
                    if subset != group:
                        node_subset = node_nodename.add_node()
                        node_subset.add_column(subset)
                        try:
                            parallel = self.conf_get("subset#"+subset, "parallel")
                        except ex.OptNotFound as exc:
                            parallel = exc.default
                        if parallel:
                            node_subset.add_column()
                            node_subset.add_column()
                            node_subset.add_column("//")
                    else:
                        node_subset = node_nodename
                    for resource in sorted(subsets[group][subset], key=lambda x: x["rid"]):
                        add_res_node(resource, node_subset, running=running)

        # discard disabled resources ?
        if self.options.show_disabled is not None:
            discard_disabled = not self.options.show_disabled
        else:
            discard_disabled = not self.show_disabled

        subsets = dispatch_resources(data)

        # service-level notices
        svc_notice = []
        if "cluster" in data:
            if data["cluster"]["overall"] == "warn":
                svc_notice.append(colorize(data["cluster"]["overall"], STATUS_COLOR[data["cluster"]["overall"]]))
            if data["cluster"]["placement"] not in ("optimal", "n/a"):
                svc_notice.append(colorize(data["cluster"]["placement"] + " placement", color.RED))
            if self.ha and not data["cluster"]["compat"]:
                svc_notice.append(colorize("incompatible versions", color.RED))
        svc_notice = ", ".join(svc_notice)

        # instance-level notices
        def instance_notice(overall=None, frozen=None, node_frozen=None, constraints=None,
                            provisioned=None, monitor=None):
            notice = []
            if overall == "warn":
                notice.append(colorize(overall, STATUS_COLOR[overall]))
            if frozen:
                notice.append(colorize("frozen", color.BLUE))
            if node_frozen:
                notice.append(colorize("node frozen", color.BLUE))
            if not constraints:
                notice.append("constraints violation")
            if provisioned is False:
                notice.append(colorize("not provisioned", color.RED))
            if monitor:
                mon_status = monitor.get("status", "unknown")
                if monitor["status"] == "idle":
                    notice.append(colorize(mon_status, color.LIGHTBLUE))
                else:
                    notice.append(colorize(mon_status, color.RED))
                if monitor.get("local_expect") not in ("", None):
                    notice.append(colorize(monitor.get("local_expect", ""), color.LIGHTBLUE))
                if monitor.get("global_expect") not in ("", None):
                    notice.append(colorize(">"+monitor.get("global_expect", ""), color.LIGHTBLUE))
            else:
                notice.append(colorize("daemon down", color.RED))
            return ", ".join(notice)

        notice = instance_notice(overall=data["overall"],
                                 frozen=self.frozen(),
                                 node_frozen=self.node.frozen(),
                                 constraints=data.get("constraints", True),
                                 provisioned=data.get("provisioned"),
                                 monitor=data.get("monitor"))

        # encap resources
        ers = {}
        for container in self.get_resources('container'):
            if container.type == "container.docker":
                continue
            try:
                ejs = data["encap"][container.rid]
                ers[container.rid] = dispatch_resources(ejs)
            except KeyError:
                continue
            except ex.excNotAvailable:
                ers[container.rid] = {}
            except Exception as exc:
                print(exc)
                ers[container.rid] = {}

        def add_res_node(resource, parent, rid=None, running=None):
            if discard_disabled and resource["disable"]:
                return
            if rid is None:
                rid = resource["rid"]
            node_res = parent.add_node()
            node_res.add_column(rid)
            node_res.add_column(fmt_flags(resource, running))
            node_res.add_column(resource["status"],
                                STATUS_COLOR[resource["status"]])
            col = node_res.add_column(resource["label"])
            if rid in ers and resource["status"] in ("up", "stdby up"):
                edata = Storage(data["encap"].get(rid))
                encap_notice = instance_notice(
                    overall=edata.overall,
                    frozen=edata.frozen,
                    constraints=edata.get("constraints", True),
                    provisioned=edata.provisioned,
                    monitor=edata.monitor,
                )
                col.add_text(encap_notice, color.LIGHTBLUE)
            for line in resource.get("log", []):
                if line.startswith("warn:"):
                    scolor = STATUS_COLOR["warn"]
                elif line.startswith("err:"):
                    scolor = STATUS_COLOR["err"]
                else:
                    scolor = None
                col.add_text(line, scolor)

            if rid not in ers or resource["status"] not in ("up", "stdby up"):
                return

            add_subsets(ers[rid], node_res, running)

        def add_instances(node):
            if len(self.peers) < 1:
                return
            for nodename in self.peers:
                if nodename == rcEnv.nodename:
                    continue
                add_instance(nodename, node)

        def add_instance(nodename, node):
            node_child = node.add_node()
            node_child.add_column(nodename, color.BOLD)
            node_child.add_column()
            try:
                mon_data = self.get_mon_data()
                data = mon_data["nodes"][nodename]["services"]["status"][self.svcname]
                avail = data["avail"]
                node_frozen = mon_data["nodes"][nodename].get("frozen")
            except KeyError as exc:
                avail = "undef"
                node_frozen = False
                data = Storage()
            node_child.add_column(avail, STATUS_COLOR[avail])
            notice = instance_notice(overall=data["overall"],
                                     frozen=data.get("frozen"),
                                     node_frozen=node_frozen,
                                     constraints=data.get("constraints", True),
                                     provisioned=data.get("provisioned"),
                                     monitor=data.get("monitor"))
            node_child.add_column(notice, color.LIGHTBLUE)

        def add_parents(node):
            if len(self.parents) == 0:
                return
            node_parents = node.add_node()
            node_parents.add_column("parents")
            for parent in self.parents:
                add_parent(parent, node_parents)

        def add_parent(svcname, node):
            node_parent = node.add_node()
            node_parent.add_column(svcname, color.BOLD)
            node_parent.add_column()
            try:
                mon_data = self.get_mon_data()
                avail = mon_data["services"][svcname]["avail"]
            except KeyError:
                avail = "undef"
            node_parent.add_column(avail, STATUS_COLOR[avail])

        def add_children(node):
            if len(self.children) == 0:
                return
            node_children = node.add_node()
            node_children.add_column("children")
            for child in self.children:
                add_child(child, node_children)

        def add_slaves(node):
            if len(self.slaves) == 0:
                return
            node_slaves = node.add_node()
            node_slaves.add_column("slaves")
            for child in self.slaves:
                add_child(child, node_slaves)

        def add_scaler_slaves(node):
            if self.scaler is None:
                return
            if self.scaler.slaves_count == 0:
                return
            node_slaves = node.add_node()
            node_slaves.add_column("scaler")
            for child in self.scaler.slaves:
                add_child(child, node_slaves)

        def add_child(svcname, node):
            node_child = node.add_node()
            node_child.add_column(svcname, color.BOLD)
            node_child.add_column()
            try:
                mon_data = self.get_mon_data()
                avail = mon_data["services"][svcname]["avail"]
            except KeyError:
                avail = "undef"
            node_child.add_column(avail, STATUS_COLOR[avail])

        tree = Forest(
            separator=" ",
            widths=(
                (14, None),
                None,
                10,
                None,
            ),
        )
        node_svcname = tree.add_node()
        node_svcname.add_column(self.svcname, color.BOLD)
        node_svcname.add_column()
        if "cluster" in data:
            node_svcname.add_column(data["cluster"]["avail"], STATUS_COLOR[data["cluster"]["avail"]])
        else:
            node_svcname.add_column()
        node_svcname.add_column(svc_notice)
        node_instances = node_svcname.add_node()
        node_instances.add_column("instances")
        add_instances(node_instances)
        node_nodename = node_instances.add_node()
        node_nodename.add_column(rcEnv.nodename, color.BOLD)
        node_nodename.add_column()
        node_nodename.add_column(data['avail'], STATUS_COLOR[data['avail']])
        node_nodename.add_column(notice, color.LIGHTBLUE)
        add_subsets(subsets, node_nodename, data["running"])
        add_parents(node_svcname)
        add_children(node_svcname)
        add_scaler_slaves(node_svcname)
        add_slaves(node_svcname)

        print(tree)

    def get_rset_status(self, groups, refresh=False):
        """
        Return the aggregated status of all resources of the specified resource
        sets, as a dict of status indexed by resourceset id.
        """
        self.setup_environ()
        rsets_status = {}
        for rset in self.get_resourcesets(groups):
            rsets_status[rset.rid] = rset.status(refresh=refresh)
        return rsets_status

    def resource_monitor(self):
        """
        The resource monitor action. Refresh important resources at a different
        schedule.
        """
        for resource in self.get_resources():
            if resource.monitor:
                resource.status(refresh=True)

    def reboot(self):
        """
        A method wrapper the node reboot method.
        """
        self.node.system.reboot()

    def crash(self):
        """
        A method wrapper the node crash method.
        """
        self.node.system.crash()

    def _pg_freeze(self):
        """
        Wrapper function for the process group freeze method.
        """
        return self._pg_freezer("freeze")

    def _pg_thaw(self):
        """
        Wrapper function for the process group thaw method.
        """
        return self._pg_freezer("thaw")

    def _pg_kill(self):
        """
        Wrapper function for the process group kill method.
        """
        return self._pg_freezer("kill")

    def _pg_freezer(self, action):
        """
        Wrapper function for the process group methods.
        """
        if not self.create_pg:
            return
        if self.pg is None:
            return
        if action == "freeze":
            self.pg.freeze(self)
        elif action == "thaw":
            self.pg.thaw(self)
        elif action == "kill":
            self.pg.kill(self)

    @lazy
    def pg(self):
        """
        A lazy property to import the system-specific process group module
        on-demand and expose it as self.pg
        """
        try:
            mod = __import__('rcPg'+rcEnv.sysname)
        except ImportError:
            self.log.info("process group are not supported on this platform")
            return
        except Exception as exc:
            print(exc)
            raise
        return mod

    def pg_freeze(self):
        """
        Freeze all process of the process groups of the service.
        """
        if self.command_is_scoped():
            self.sub_set_action(["app", "container"], "_pg_freeze")
        else:
            self._pg_freeze()
            for resource in self.get_resources(["app", "container"]):
                resource.status(refresh=True)

    def pg_thaw(self):
        """
        Thaw all process of the process groups of the service.
        """
        if self.command_is_scoped():
            self.sub_set_action(["app", "container"], "_pg_thaw")
        else:
            self._pg_thaw()
            for resource in self.get_resources(["app", "container"]):
                resource.status(refresh=True)

    def pg_kill(self):
        """
        Kill all process of the process groups of the service.
        """
        if self.command_is_scoped():
            self.sub_set_action(["app", "container"], "_pg_kill")
        else:
            self._pg_kill()
            for resource in self.get_resources(["app", "container"]):
                resource.status(refresh=True)

    def do_pre_monitor_action(self):
        if self.pre_monitor_action is None:
            return
        kwargs = {}
        if "|" in self.pre_monitor_action or \
           "&&" in self.pre_monitor_action or \
           ";" in self.pre_monitor_action:
            kwargs["shell"] = True

        import shlex
        if not kwargs.get("shell", False):
            if sys.version_info[0] < 3:
                cmdv = shlex.split(self.pre_monitor_action.encode('utf8'))
                cmdv = [elem.decode('utf8') for elem in cmdv]
            else:
                cmdv = shlex.split(self.pre_monitor_action)
        else:
            cmdv = self.pre_monitor_action

        try:
            result = self.vcall(cmdv, **kwargs)
            ret = result[0]
        except OSError as exc:
            ret = 1
            if exc.errno == 8:
                self.log.error("%s exec format error: check the script shebang", self.pre_monitor_action)
            else:
                self.log.error("%s error: %s", self.pre_monitor_action, str(exc))
        except Exception as exc:
            ret = 1
            self.log.error("%s error: %s", self.pre_monitor_action, str(exc))

    def toc(self):
        """
        Call the resource monitor action.
        """
        if self.frozen():
            self.log.info("refuse to toc from a frozen service")
            return
        self.do_pre_monitor_action()
        if self.monitor_action is None:
            return
        if not hasattr(self, self.monitor_action):
            self.log.error("invalid monitor action '%s'", self.monitor_action)
            return
        self.log.info("start monitor action '%s'", self.monitor_action)
        getattr(self, self.monitor_action)()

    def encap_cmd(self, cmd, verbose=False, error="raise"):
        """
        Execute a command in all service containers.
        If error is set to "raise", stop iterating at first error.
        If error is set to "continue", log errors and proceed to the next
        container.
        """
        for container in self.get_resources('container'):
            try:
                self._encap_cmd(cmd, container, verbose=verbose)
            except ex.excEncapUnjoignable:
                if error != "continue":
                    self.log.error("container %s is not joinable to execute "
                                   "action '%s'", container.name, ' '.join(cmd))
                    raise
                elif verbose:
                    self.log.warning("container %s is not joinable to execute "
                                     "action '%s'", container.name, ' '.join(cmd))

    def _encap_cmd(self, cmd, container, verbose=False, push_config=True):
        """
        Execute a command in a service container.
        """
        if container.pg_frozen():
            raise ex.excError("can't join a frozen container. abort encap "
                              "command.")
        if cmd == ["start"] and container.booted:
            return '', '', 0
        if not self.has_encap_resources:
            self.log.debug("skip encap %s: no encap resource", ' '.join(cmd))
            return '', '', 0
        if not container.is_up():
            msg = "skip encap %s: the container is not running here" % ' '.join(cmd)
            if verbose:
                self.log.info(msg)
            else:
                self.log.debug(msg)
            return '', '', 0

        if self.options.slave is not None and not \
           (container.name in self.options.slave or \
            container.rid in self.options.slave):
            # no need to run encap cmd (container not specified in --slave)
            return '', '', 0

        if cmd == ['start'] and not self.command_is_scoped():
            return '', '', 0

        # make sure the container has an up-to-date service config
        if push_config:
            try:
                self._push_encap_config(container)
            except ex.excError:
                pass

        # now we known we'll execute a command in the slave, so purge the
        # encap cache
        self.purge_cache_encap_json_status(container.rid)

        # wait for the container multi-user state
        if cmd[0] in ["start"] and hasattr(container, "wait_multi_user"):
            container.wait_multi_user()

        options = ['--daemon']
        if self.options.dry_run:
            options.append('--dry-run')
        if self.options.force:
            options.append('--force')
        if self.options.local:
            options.append('--local')
        if self.options.disable_rollback:
            options.append('--disable-rollback')
        if self.options.rid:
            options.append('--rid')
            options.append(self.options.rid)
        if self.options.tags:
            options.append('--tags')
            options.append(self.options.tags)
        if self.options.subsets:
            options.append('--subsets')
            options.append(self.options.subsets)

        paths = get_osvc_paths(osvc_root_path=container.osvc_root_path,
                               sysname=container.guestos)
        cmd = [paths.svcmgr, '-s', self.svcname] + options + cmd

        if container is not None and hasattr(container, "rcmd"):
            out, err, ret = container.rcmd(cmd)
        elif hasattr(container, "runmethod"):
            cmd = container.runmethod + cmd
            out, err, ret = justcall(cmd, stdin=self.node.devnull)
        else:
            raise ex.excEncapUnjoignable("undefined rcmd/runmethod in "
                                         "resource %s"%container.rid)

        if verbose:
            self.log.info('logs from %s child service:', container.name)
            print(out)
            if len(err) > 0:
                print(err)
        if ret == 255:
            raise ex.excEncapUnjoignable
        if ret != 0:
            raise ex.excError("error from encap service command '%s': "
                              "%d\n%s\n%s"%(' '.join(cmd), ret, out, err))
        return out, err, ret

    def get_encap_json_status_path(self, rid):
        """
        Return the path of the file where the status data of the service
        encapsulated in the container identified by <rid> will be written
        for caching.
        """
        return os.path.join(self.var_d, rid, "encap.status.json")

    def purge_cache_encap_json_status(self, rid):
        """
        Delete the on-disk cache of status of the service encapsulated in
        the container identified by <rid>.
        """
        if rid in self.encap_json_status_cache:
            del self.encap_json_status_cache[rid]
        path = self.get_encap_json_status_path(rid)
        if os.path.exists(path):
            os.unlink(path)

    def put_cache_encap_json_status(self, rid, data):
        """
        Write the on-disk cache of status of the service encapsulated in
        the container identified by <rid>.
        """
        self.encap_json_status_cache[rid] = data
        path = self.get_encap_json_status_path(rid)
        directory = os.path.dirname(path)
        if not os.path.exists(directory):
            os.makedirs(directory)
        try:
            with open(path, 'w') as ofile:
                ofile.write(json.dumps(data))
                ofile.flush()
        except (IOError, OSError, ValueError):
            os.unlink(path)

    def get_cache_encap_json_status(self, rid):
        """
        Fetch the on-disk cache of status of the service encapsulated in
        the container identified by <rid>.
        """
        if rid in self.encap_json_status_cache:
            return self.encap_json_status_cache[rid]
        path = self.get_encap_json_status_path(rid)
        try:
            with open(path, 'r') as ofile:
                group_status = json.loads(ofile.read())
        except (IOError, OSError, ValueError):
            group_status = None
        return group_status

    @lazy
    def encap_groups(self):
        from svcdict import DEPRECATED_SECTIONS
        egroups = set()
        for rid in self.encap_resources:
            egroup = rid.split('#')[0]
            if egroup in DEPRECATED_SECTIONS:
                egroup = DEPRECATED_SECTIONS[egroup][0]
            egroups.add(egroup)
        return egroups

    def encap_json_status(self, container, refresh=False):
        """
        Return the status data from the agent runnning the encapsulated part
        of the service.
        """
        if container.guestos == 'windows':
            raise ex.excNotAvailable

        if container.status(ignore_nostatus=True, refresh=refresh) == rcStatus.DOWN:
            #
            #  passive node for the vservice => forge encap resource status
            #    - encap sync are n/a
            #    - other encap res are down
            #
            group_status = {
                "avail": "down",
                "overall": "down",
                "type": container.type,
                "frozen": False,
                "resources": {},
            }
            groups = set(["container", "ip", "disk", "fs", "share"])
            for group in groups:
                if group in self.encap_groups:
                    group_status[group] = 'down'
                else:
                    group_status[group] = 'n/a'
            for resource in self.get_resources(groups):
                group = resource.type.split('.')[0]
                if group not in groups:
                    continue
                if not self.encap and resource.encap:
                    group_status['resources'][resource.rid] = {'status': 'down'}

            groups = set(["app", "sync"])
            for group in groups:
                group_status[group] = 'n/a'
            for resource in self.get_resources(groups):
                group = resource.type.split('.')[0]
                if group not in groups:
                    continue
                if not self.encap and resource.encap:
                    group_status['resources'][resource.rid] = {'status': 'n/a'}

            return group_status

        if not refresh:
            group_status = self.get_cache_encap_json_status(container.rid)
            if group_status:
                return group_status

        group_status = {
            "avail": "n/a",
            "overall": "n/a",
            "type": container.type,
            "frozen": False,
            "resources": {},
        }
        groups = set([
            "container",
            "ip",
            "disk",
            "fs",
            "share",
            "task",
            "app",
            "sync"
        ])

        for group in groups:
            group_status[group] = 'n/a'

        cmd = ['print', 'status', '--format', 'json']
        if refresh:
            cmd.append('--refresh')
        try:
            results = self._encap_cmd(cmd, container)
        except ex.excError:
            return group_status
        except Exception as exc:
            print(exc)
            return group_status

        try:
            group_status = json.loads(results[0])
        except:
            pass

        self.put_cache_encap_json_status(container.rid, group_status)

        return group_status

    def group_status(self, groups=None, excluded_groups=None, refresh=False):
        """
        Return the status data of the service.
        """
        if excluded_groups is None:
            excluded_groups = set()
        if groups is None:
            groups = set(DEFAULT_STATUS_GROUPS)

        status = {
            "status_group": {},
        }
        groups = groups - excluded_groups
        self.get_rset_status(groups, refresh=refresh)

        # initialise status of each group
        for group in TOP_STATUS_GROUPS:
            status[group] = rcStatus.Status(rcStatus.NA)
        for group in groups:
            status["status_group"][group] = rcStatus.Status(rcStatus.NA)

        for driver in STATUS_TYPES:
            if driver in excluded_groups:
                continue
            group = driver.split('.')[0]
            if group not in groups:
                continue
            for resource in self.get_resources(driver):
                rstatus = resource.status()
                if resource.type.startswith("sync"):
                    if rstatus == rcStatus.UP:
                        rstatus = rcStatus.NA
                    elif rstatus == rcStatus.DOWN:
                        rstatus = rcStatus.WARN
                status["status_group"][group] += rstatus
                if resource.is_optional():
                    status["optional"] += rstatus
                else:
                    status["avail"] += rstatus
                if resource.status_logs_count(levels=["warn", "error"]) > 0:
                    status["overall"] += rcStatus.WARN

        if status["avail"].status == rcStatus.STDBY_UP_WITH_UP:
            # now we know the avail status we can promote
            # stdbyup to up
            status["avail"].status = rcStatus.UP
            for group in status:
                if status[group] == rcStatus.STDBY_UP:
                    status[group].status = rcStatus.UP
        elif status["avail"].status == rcStatus.STDBY_UP_WITH_DOWN:
            status["avail"].status = rcStatus.STDBY_UP

        if status["optional"].status == rcStatus.STDBY_UP_WITH_UP:
            status["optional"].status = rcStatus.UP
        elif status["optional"].status == rcStatus.STDBY_UP_WITH_DOWN:
            status["optional"].status = rcStatus.STDBY_UP

        status["overall"] += rcStatus.Status(status["avail"])
        status["overall"] += rcStatus.Status(status["optional"])

        return status

    def print_exposed_devs(self):
        self.print_devs(categories=["exposed"])

    def print_sub_devs(self):
        self.print_devs(categories=["sub"])

    def print_base_devs(self):
        self.print_devs(categories=["base"])

    def print_devs(self, categories=None):
        """
        Print the list of devices the service exposes.
        """
        if categories is None:
            categories = ["exposed", "sub", "base"]
        data = self.devs(categories=categories)
        if self.options.format is not None:
            return data
        from forest import Forest
        from rcColor import color
        tree = Forest()
        node1 = tree.add_node()
        node1.add_column(self.svcname, color.BOLD)
        for rid, _data in data.items():
            node = node1.add_node()
            text = "%s (%s)" % (rid, _data["type"])
            node.add_column(text, color.BROWN)
            for cat, __data in _data.items():
                if cat == "type":
                    continue
                catnode = node.add_node()
                catnode.add_column(cat, color.LIGHTBLUE)
                for dev in __data:
                    devnode = catnode.add_node()
                    devnode.add_column(dev)
        print(tree)

    def devs(self, categories=None):
        """
        Return the list of devices the service exposes.
        """
        if categories is None:
            categories = ("exposed", "sub", "base")
        data = {}
        if self.action_rid == []:
            resources = self.get_resources()
        else:
            resources = [self.get_resource(rid) for rid in self.action_rid]
        for resource in resources:
            if resource is None:
                continue
            for cat in categories:
                devs = sorted(list(getattr(resource, cat+"_devs")()))
                if len(devs) == 0:
                    continue
                if resource.rid not in data:
                    data[resource.rid] = {}
                data[resource.rid]["type"] = resource.type
                data[resource.rid][cat] = devs
        return data

    def sub_devs(self):
        """
        Return the list of sub devices of each resource, aggregated as a single
        list. Used by the checkers to parent checks to a service.
        """
        data = self.devs(categories=["sub"])
        devs = set()
        for rid, _data in data.items():
            devs |= set(_data.get("sub", []))
        return devs

    def exposed_devs(self):
        """
        Return the list of exposed devices of each resource, aggregated as a
        single list. Used by the fs unprovisioner.
        """
        data = self.devs(categories=["exposed"])
        devs = set()
        for rid, _data in data.items():
            devs |= set(_data.get("exposed", []))
        return devs

    def print_config_mtime(self):
        """
        Print the service configuration file last modified timestamp. Used by
        remote agents to determine which agent holds the most recent version.
        """
        mtime = os.stat(self.paths.cf).st_mtime
        print(mtime)

    def prepare_async_cmd(self):
        cmd = sys.argv[1:]
        cmd = drop_option("--node", cmd, drop_value=True)
        cmd = drop_option("-s", cmd, drop_value=True)
        cmd = drop_option("--service", cmd, drop_value=True)
        return cmd

    def async_action(self, action, wait=None, timeout=None):
        if action in ACTION_NO_ASYNC:
            return
        if self.options.node is not None and self.options.node != "":
            cmd = self.prepare_async_cmd()
            ret = self.daemon_service_action(cmd)
            if ret == 0:
                raise ex.excAbortAction()
            else:
                raise ex.excError()
        if self.options.local or self.options.slave or self.options.slaves or \
           self.options.master:
            return
        if action not in ACTION_ASYNC:
            return
        if "target" not in ACTION_ASYNC[action]:
            return
        if self.command_is_scoped():
            return
        self.daemon_mon_action(action, wait=wait, timeout=timeout)
        raise ex.excAbortAction()

    def daemon_mon_action(self, action, wait=None, timeout=None, svcname=None):
        if svcname is None:
            svcname = self.svcname
        self.validate_mon_action(action)
        global_expect = ACTION_ASYNC[action]["target"]
        if action == "delete" and self.options.unprovision:
            global_expect = "purged"
            action = "purge"
        self.set_service_monitor(global_expect=global_expect, svcname=svcname)
        if svcname == self.svcname:
            self.log.info("%s action requested", action)
        else:
            self.log.info("%s action requested on service %s", action, svcname)
        if wait is None:
            wait = self.options.wait
        if timeout is None:
            timeout = self.options.time
        if timeout is not None:
            timeout = convert_duration(timeout)
        if not wait:
            return

        # poll global service status
        prev_global_expect_set = set()
        for _ in range(timeout):
            data = self.node._daemon_status(refresh=True)
            if data is None or "monitor" not in data:
                # interrupted, daemon died
                time.sleep(1)
                continue
            global_expect_set = set()
            inprogress = []
            for nodename in data["monitor"]["nodes"]:
                try:
                    _data = data["monitor"]["nodes"][nodename]["services"]["status"][svcname]
                except (KeyError, TypeError) as exc:
                    continue
                if _data["monitor"].get("global_expect") is not None:
                    inprogress.append(nodename)
                if _data["monitor"].get("global_expect") in (global_expect, "n/a"):
                    global_expect_set.add(nodename)
            if prev_global_expect_set != global_expect_set:
                for nodename in global_expect_set - prev_global_expect_set:
                    self.log.info(" work starting on %s", nodename)
                for nodename in prev_global_expect_set - global_expect_set:
                    self.log.info(" work over on %s", nodename)
            if not inprogress and not global_expect_set:
                if svcname not in data["monitor"]["services"]:
                    return
                self.log.info("final status: avail=%s overall=%s frozen=%s",
                              data["monitor"]["services"][svcname]["avail"],
                              data["monitor"]["services"][svcname]["overall"],
                              data["monitor"]["services"][svcname].get("frozen", False))
                return
            prev_global_expect_set = set(global_expect_set)
            time.sleep(1)
        raise ex.excError("wait timeout exceeded")

    def current_node(self):
        data = self.node._daemon_status()
        for nodename, _data in data["monitor"]["nodes"].items():
            try:
                __data = _data["services"]["status"][self.svcname]
            except KeyError:
                continue
            if __data["avail"] == "up":
                return nodename

    def command_is_scoped(self, options=None):
        """
        Return True if a resource filter has been setup through
        --rid, --subsets or --tags
        """
        if options is None:
            options = self.options
        if (options.rid is not None and options.rid != "") or \
           (options.tags is not None and options.tags != "") or \
           (options.subsets is not None and options.subsets != "") or \
           options.upto or options.downto:
            return True
        return False

    def run(self):
        self.master_run()
        self.slave_run()

    @_master_action
    def master_run(self):
        self.sub_set_action("task", "run")

    @_slave_action
    def slave_run(self):
        self.encap_cmd(['run'], verbose=True)

    def startstandby(self):
        self.master_startstandby()
        self.slave_startstandby()

    @_master_action
    def master_startstandby(self):
        self.sub_set_action(START_GROUPS, "startstandby", xtags=set(["zone", "docker"]))

    @_slave_action
    def slave_startstandby(self):
        cmd = self.prepare_async_cmd()
        self.encap_cmd(cmd, verbose=True)

    def start(self):
        self.abort_start()
        self.master_start()
        self.slave_start()

    @_master_action
    def master_start(self):
        self.sub_set_action(START_GROUPS, "start", xtags=set(["zone", "docker"]))

    @_slave_action
    def slave_start(self):
        cmd = self.prepare_async_cmd()
        self.encap_cmd(cmd, verbose=True)

    def rollback(self):
        self.sub_set_action(STOP_GROUPS, "rollback", xtags=set(["zone", "docker"]))

    def stop(self):
        self.slave_stop()
        self.master_stop()

    @_master_action
    def master_stop(self):
        self.sub_set_action(STOP_GROUPS, "stop", xtags=set(["zone", "docker"]))

    @_slave_action
    def slave_stop(self):
        self.encap_cmd(['stop'], verbose=True, error="continue")

    def shutdown(self):
        self.options.force = True
        self.slave_shutdown()
        self.master_shutdown()

    @_master_action
    def master_shutdown(self):
        self.sub_set_action(STOP_GROUPS, "shutdown", xtags=set(["zone", "docker"]))

    @_slave_action
    def slave_shutdown(self):
        self.encap_cmd(['shutdown'], verbose=True, error="continue")

    def unprovision(self):
        self.sub_set_action(STOP_GROUPS, "unprovision", xtags=set(["zone", "docker"]))

    def provision(self):
        self.sub_set_action(START_GROUPS, "provision", xtags=set(["zone", "docker"]))

        if not self.options.disable_rollback:
            # set by the daemon on the placement leaders.
            # return the service to standby if not a placement leader
            self.rollback()

    def set_provisioned(self):
        self.sub_set_action(START_GROUPS, "set_provisioned", xtags=set(["zone", "docker"]))

    def set_unprovisioned(self):
        self.sub_set_action(START_GROUPS, "set_unprovisioned", xtags=set(["zone", "docker"]))

    def abort_start(self):
        """
        Give a chance to all resources concerned by the action to voice up
        their rebutal of the action before it begins.
        """
        self.abort_start_done = True
        if rcEnv.sysname == "Windows":
            parallel = False
        else:
            try:
                from multiprocessing import Process
                parallel = True
                def wrapper(func):
                    if func():
                        sys.exit(1)
            except ImportError:
                parallel = False

        procs = {}
        for resource in self.get_resources():
            if resource.skip or resource.is_disabled():
                continue
            if not hasattr(resource, 'abort_start'):
                continue
            if not parallel:
                if resource.abort_start():
                    raise ex.excError("start aborted due to resource %s "
                                      "conflict" % resource.rid)
            else:
                proc = Process(target=wrapper, args=[resource.abort_start])
                proc.start()
                procs[resource.rid] = proc

        if parallel:
            err = []
            for rid, proc in procs.items():
                proc.join()
                if proc.exitcode > 0:
                    err.append(rid)
            if len(err) > 0:
                raise ex.excError("start aborted due to resource %s "
                                  "conflict" % ",".join(err))

    def refresh_ip_status(self):
        """ Used after start/stop container because the ip resource
            status change after its own start/stop
        """
        for resource in self.get_resources("ip"):
            resource.status(refresh=True)

    def dns_update(self):
        """
        Call the dns update method of each resource.
        """
        self.all_set_action("dns_update")

    def postsync(self):
        """ action triggered by a remote master node after
            sync_nodes and sync_drp. Typically make use of files
            received in var/
        """
        self.all_set_action("postsync")

    def remote_postsync(self):
        """ Release the svc lock at this point because the
            waitlock timeout is long and we are done touching
            local data.

            Action triggered by a remote master node after
            sync_nodes and sync_drp. Typically make use of files
            received in var/.
            use a long waitlock timeout to give a chance to
            remote syncs to finish
        """
        self.svcunlock()
        for nodename in self.need_postsync:
            self.daemon_service_action(['postsync', '--waitlock=3600'],
                                       nodename=nodename, sync=False,
                                       collect=False)

        self.need_postsync = set()

    def remote_action(self, nodename, action, waitlock=DEFAULT_WAITLOCK,
                      sync=False, verbose=True, action_mode=True, collect=True):
        rcmd = []
        if self.options.debug:
            rcmd += ['--debug']
        if self.options.dry_run:
            rcmd += ['--dry-run']
        if self.options.local and action_mode:
            rcmd += ['--local']
        if self.options.cron:
            rcmd += ['--cron']
        if self.options.waitlock != DEFAULT_WAITLOCK:
            rcmd += ['--waitlock', str(waitlock)]
        rcmd += action.split()
        if verbose:
            self.log.info("exec '%s' on node %s", ' '.join(rcmd), nodename)
        return self.daemon_service_action(rcmd, nodename=nodename, sync=sync,
                                          collect=collect, action_mode=action_mode)

    def presync(self):
        """ prepare files to send to slave nodes in var/.
            Each resource can prepare its own set of files.
        """
        self.need_postsync = set()
        if self.presync_done:
            return
        self.all_set_action("presync")
        self.presync_done = True

    def sync_nodes(self):
        rtypes = [
            "sync.rsync",
            "sync.zfs",
            "sync.btrfs",
            "sync.docker",
            "sync.dds",
        ]
        if not self.can_sync(rtypes, 'nodes'):
            return
        self.presync()
        self.sub_set_action(rtypes, "sync_nodes")
        self.remote_postsync()

    def sync_drp(self):
        rtypes = [
            "sync.rsync",
            "sync.zfs",
            "sync.btrfs",
            "sync.docker",
            "sync.dds",
        ]
        if not self.can_sync(rtypes, 'drpnodes'):
            return
        self.presync()
        self.sub_set_action(rtypes, "sync_drp")
        self.remote_postsync()

    def sync_swap(self):
        rtypes = [
            "sync.netapp",
            "sync.symsrdfs",
            "sync.hp3par",
            "sync.nexenta",
        ]
        self.sub_set_action(rtypes, "sync_swap")

    def sync_revert(self):
        rtypes = [
            "sync.hp3par",
        ]
        self.sub_set_action(rtypes, "sync_revert")

    def sync_resume(self):
        rtypes = [
            "sync.netapp",
            "sync.symsrdfs",
            "sync.hp3par",
            "sync.dcsckpt",
            "sync.nexenta",
        ]
        self.sub_set_action(rtypes, "sync_resume")

    def sync_quiesce(self):
        rtypes = [
            "sync.netapp",
            "sync.nexenta",
        ]
        self.sub_set_action(rtypes, "sync_quiesce")

    def resync(self):
        self.stop()
        self.sync_resync()
        self.start()

    def sync_resync(self):
        rtypes = [
            "sync.netapp",
            "sync.nexenta",
            "sync.radossnap",
            "sync.radosclone",
            "sync.dds",
            "sync.symclone",
            "sync.symsnap",
            "sync.ibmdssnap",
            "sync.evasnap",
            "sync.necismsnap",
            "sync.dcssnap",
            "disk.md",
        ]
        self.sub_set_action(rtypes, "sync_resync")

    def sync_break(self):
        rtypes = [
            "sync.netapp",
            "sync.nexenta",
            "sync.hp3par",
            "sync.dcsckpt",
            "sync.symclone",
            "sync.symsnap",
        ]
        self.sub_set_action(rtypes, "sync_break")

    def sync_update(self):
        rtypes = [
            "sync.netapp",
            "sync.nexenta",
            "sync.hp3par",
            "sync.hp3parsnap",
            "sync.dcsckpt",
            "sync.dds",
            "sync.btrfssnap",
            "sync.zfs",
            "sync.zfssnap",
            "sync.s3",
            "sync.symclone",
            "sync.symsnap",
            "sync.ibmdssnap",
        ]
        self.sub_set_action(rtypes, "sync_update")

    def sync_full(self):
        rtypes = [
            "sync.dds",
            "sync.zfs",
            "sync.btrfs",
            "sync.s3",
        ]
        self.sub_set_action(rtypes, "sync_full")

    def sync_restore(self):
        rtypes = [
            "sync.s3",
            "sync.symclone",
            "sync.symsnap",
        ]
        self.sub_set_action(rtypes, "sync_restore")

    def sync_split(self):
        rtypes = [
            "sync.symsrdfs",
        ]
        self.sub_set_action(rtypes, "sync_split")

    def sync_establish(self):
        rtypes = [
            "sync.symsrdfs",
        ]
        self.sub_set_action(rtypes, "sync_establish")

    def sync_verify(self):
        rtypes = [
            "sync.dds",
        ]
        self.sub_set_action(rtypes, "sync_verify")

    def print_config(self):
        """
        The 'print config' action entry point.
        Print the service configuration in the format specified by --format.
        """
        if self.options.format is not None:
            return self.print_config_data(evaluate=self.options.eval,
                                          impersonate=self.options.impersonate)
        from rcColor import print_color_config
        print_color_config(self.paths.cf)

    def make_temp_config(self):
        """
        Copy the current service configuration file to a temporary
        location for edition.
        If the temp file already exists, propose the --discard
        or --recover options.
        """
        import shutil
        if os.path.exists(self.paths.tmp_cf):
            if self.options.recover:
                pass
            elif self.options.discard:
                shutil.copy(self.paths.cf, self.paths.tmp_cf)
            else:
                self.edit_config_diff()
                print("%s exists: service is already being edited. Set "
                      "--discard to edit from the current configuration, "
                      "or --recover to open the unapplied config" % \
                      self.paths.tmp_cf, file=sys.stderr)
                raise ex.excError
        else:
            shutil.copy(self.paths.cf, self.paths.tmp_cf)
        return self.paths.tmp_cf

    def edit_config_diff(self):
        """
        Display the diff between the current config and the pending
        unvalidated config.
        """
        from subprocess import call

        def diff_capable(opts):
            cmd = ["diff"] + opts + [self.paths.cf, self.paths.cf]
            cmd_results = justcall(cmd)
            if cmd_results[2] == 0:
                return True
            return False

        if not os.path.exists(self.paths.tmp_cf):
            return
        if diff_capable(["-u", "--color"]):
            cmd = ["diff", "-u", "--color", self.paths.cf, self.paths.tmp_cf]
        elif diff_capable(["-u"]):
            cmd = ["diff", "-u", self.paths.cf, self.paths.tmp_cf]
        else:
            cmd = ["diff", self.paths.cf, self.paths.tmp_cf]
        call(cmd)

    def edit_config(self):
        """
        Execute an editor on the service configuration file.
        When the editor exits, validate the new configuration file.
        If validation pass, install the new configuration,
        else keep the previous configuration in place and offer the
        user the --recover or --discard choices for its next edit
        config action.
        """
        if "EDITOR" in os.environ:
            editor = os.environ["EDITOR"]
        elif os.name == "nt":
            editor = "notepad"
        else:
            editor = "vi"
        from rcUtilities import which, fsum
        if not which(editor):
            print("%s not found" % editor, file=sys.stderr)
            return 1
        path = self.make_temp_config()
        os.environ["LANG"] = "en_US.UTF-8"
        os.system(' '.join((editor, path)))
        if fsum(path) == fsum(self.paths.cf):
            os.unlink(path)
            return 0
        results = self._validate_config(path=path)
        if results["errors"] == 0:
            import shutil
            shutil.copy(path, self.paths.cf)
            os.unlink(path)
        else:
            print("your changes were not applied because of the errors "
                  "reported above. you can use the edit config command "
                  "with --recover to try to fix your changes or with "
                  "--discard to restart from the live config")
        return results["errors"] + results["warnings"]

    def can_sync(self, rtypes=None, target=None):
        """
        Return True if any resource of type in <rtypes> yields it can sync.
        """
        if rtypes is None:
            rtypes = []
        ret = False
        for rtype in rtypes:
            for resource in self.get_resources(rtype):
                try:
                    ret |= resource.can_sync(target)
                    self.log.debug("resource %s can sync: %s" % (resource.rid, str(ret)))
                except ex.excError as exc:
                    return False
                if ret:
                    return True
        self.log.debug("nothing to sync for the service for now")
        return False

    def sync_all(self):
        """
        The 'sync all' action entrypoint.
        """
        if not self.can_sync(["sync"]):
            return
        self.sync_update()
        self.presync()
        rtypes = [
            "sync.rsync",
            "sync.btrfs",
            "sync.docker",
            "sync.dds",
        ]
        self.sub_set_action(rtypes, "sync_nodes")
        self.sub_set_action(rtypes, "sync_drp")
        self.remote_postsync()

    def service_status(self):
        """
        The 'service_status' scheduler task and action entrypoint.

        This method returns early if called from an encapsulated agent, as
        the master agent is responsible for pushing the encapsulated
        status.
        """
        if self.encap:
            if not self.options.cron:
                self.log.info("push service status is disabled for encapsulated services")
            return
        self.print_status_data(mon_data=False, refresh=True)

    def resinfo(self):
        """
        Return a list of (key, val) for each service resource and
        env section.
        """
        data = {}
        for res in self.get_resources():
            try:
                _data = res.info()
            except Exception as exc:
                _data = []
                print(exc, file=sys.stderr)
            for __data in _data:
                rid = __data[-3]
                if rid not in data:
                    data[rid] = []
                data[rid].append(__data)
        if not self.config.has_section("env"):
            return data
        for key in self.config.options("env"):
            try:
                val = self.conf_get("env", key)
            except ex.OptNotFound as exc:
                continue
            if "env" not in data:
                data["env"] = []
            data["env"].append([
                self.svcname,
                rcEnv.nodename,
                self.topology,
                "env",
                key if key is not None else "",
                val if val is not None else "",
            ])
        return data

    def print_resinfo(self):
        if self.options.format is None:
            self.print_resinfo_tree()
            return
        data = [[
            'res_svcname',
            'res_nodename',
            'topology',
            'rid',
            'res_key',
            'res_value',
        ]]
        for _data in self.resinfo().values():
            data += _data
        return data

    def print_resinfo_tree(self):
        from forest import Forest
        from rcColor import color
        tree = Forest()
        node1 = tree.add_node()
        node1.add_column(self.svcname, color.BOLD)
        data = self.resinfo()
        for rid in sorted(data.keys()):
            _data = data[rid]
            node = node1.add_node()
            text = "%s" % rid
            node.add_column(text, color.BROWN)
            for __data in _data:
                catnode = node.add_node()
                catnode.add_column(__data[-2], color.LIGHTBLUE)
                catnode.add_column(__data[-1])
        print(tree)

    def push_config(self):
        """
        Push the service config to the collector. Usually done
        automatically by the collector thread.
        """
        self.node.collector.call('push_config', self)

    def push_resinfo(self):
        """
        The 'push_resinfo' scheduler task and action entrypoint.
        Push the per-resource key/value pairs to the collector.
        """
        data = []
        for _data in self.resinfo().values():
            data += _data
        self.node.collector.call('push_resinfo', data)

    def push_encap_config(self):
        """
        Synchronize the configuration file between encap and master agent,
        This action is skipped when run by an encapsulated agent.

        Verify the service has an encapsulated part, and if so, for each
        container in up state running an encapsulated part, synchronize the
        service configuration file.
        """
        if self.encap or not self.has_encap_resources:
            return

        for resource in self.get_resources('container'):
            if resource.status(ignore_nostatus=True) not in (rcStatus.STDBY_UP, rcStatus.UP):
                continue
            self._push_encap_config(resource)

    def _push_encap_config(self, container):
        if len(self.log.handlers) > 1:
            self.log.handlers[1].setLevel(logging.CRITICAL)
        try:
            self.__push_encap_config(container)
        finally:
            if len(self.log.handlers) > 1:
                self.log.handlers[1].setLevel(rcEnv.loglevel)

    def __push_encap_config(self, container):
        """
        Compare last modification time of the master and slave service
        configuration file, and copy the most recent version over the least
        recent.
        """
        cmd = ['print', 'config', 'mtime']
        try:
            cmd_results = self._encap_cmd(cmd, container, push_config=False)
            out = cmd_results[0]
            ret = cmd_results[2]
        except ex.excError:
            out = None
            ret = 1

        paths = get_osvc_paths(osvc_root_path=container.osvc_root_path,
                               sysname=container.guestos)
        encap_cf = os.path.join(paths.pathetc, os.path.basename(self.paths.cf))

        if out == "":
            # this is what happens when the container is down
            return

        if ret == 0:
            encap_mtime = int(float(out.strip()))
            local_mtime = int(os.stat(self.paths.cf).st_mtime)
            if encap_mtime > local_mtime:
                if hasattr(container, 'rcp_from'):
                    cmd_results = container.rcp_from(encap_cf, rcEnv.paths.pathetc+'/')
                else:
                    cmd = rcEnv.rcp.split() + [container.name+':'+encap_cf, rcEnv.paths.pathetc+'/']
                    cmd_results = justcall(cmd)
                os.utime(self.paths.cf, (encap_mtime, encap_mtime))
                self.log.info("fetch %s from %s", encap_cf, container.name)
                if cmd_results[2] != 0:
                    raise ex.excError()
                return
            elif encap_mtime == local_mtime:
                return

        if hasattr(container, 'rcp'):
            cmd_results = container.rcp(self.paths.cf, encap_cf)
        else:
            cmd = rcEnv.rcp.split() + [self.paths.cf, container.name+':'+encap_cf]
            cmd_results = justcall(cmd)
        if cmd_results[2] != 0:
            raise ex.excError("failed to send %s to %s" % (self.paths.cf, container.name))
        self.log.info("send %s to %s", self.paths.cf, container.name)

        cmd = ['create', '--config', encap_cf]
        cmd_results = self._encap_cmd(cmd, container=container, push_config=False)
        if cmd_results[2] != 0:
            raise ex.excError("failed to create %s slave service" % container.name)
        self.log.info("create %s slave service", container.name)

    @staticmethod
    def _tag_match(rtags, keeptags):
        """
        Return True if any tag of <rtags> is in <keeptags>.
        """
        for tag in rtags:
            if tag in keeptags:
                return True
        return False

    def set_skip_resources(self, keeprid=None, xtags=None):
        """
        Set the 'skip' flag of all resources.
        * set to False if keeprid is empty and xtags is empty
        * set to False if rid is in keeprid and not in xtags
        * else set to True
        """
        if keeprid is None:
            keeprid = []

        if xtags is None:
            xtags = set()

        ridfilter = len(keeprid) > 0
        tagsfilter = len(xtags) > 0

        if not tagsfilter and not ridfilter:
            return

        for resource in self.get_resources():
            if self._tag_match(resource.tags, xtags):
                resource.skip = True
            if ridfilter and resource.rid in keeprid:
                continue
            resource.skip = True

    def setup_environ(self, action=None):
        """
        Setup envionment variables.
        Startup scripts and triggers can use them, so their code can be
        more generic.
        All resources can contribute a set of env variables through their
        own setup_environ() method.
        """
        os.environ['OPENSVC_SVCNAME'] = self.svcname
        if action:
            os.environ['OPENSVC_ACTION'] = action
        for resource in self.get_resources():
            resource.setup_environ()

    def all_rids(self):
        return [rid for rid in self.resources_by_id if rid is not None] + \
               list(self.encap_resources.keys())

    def expand_rid(self, rid):
        """
        Given a rid return a set containing either the rid itself if it is
        a known rid, or containing the rid of all resources whose prefix
        matches the name given as rid.
        """
        retained_rids = set()
        for _rid in self.all_rids():
            if '#' in rid:
                if _rid == rid:
                    retained_rids.add(_rid)
                else:
                    continue
            elif '.' in rid:
                if _rid in self.resources_by_id and rid == self.resources_by_id[_rid].type:
                    retained_rids.add(_rid)
                elif _rid in self.encap_resources and rid == self.encap_resources[_rid].type:
                    retained_rids.add(_rid)
            elif _rid[:_rid.index('#')] == rid:
                retained_rids.add(_rid)
        return retained_rids

    def expand_rids(self, rids):
        """
        Parse the --rid value and return the retained corresponding resource
        ids.
        Filter out non existing resource ids.
        If a rid has no "#", expand to the set of rids of resources whose
        prefix matches the name given as a rid.

        Example:
        --rid disk: return all rids of disk resources.
        --rid disk#0: return disk#0 if such a resource exists
        """
        if len(rids) == 0:
            return
        retained_rids = set()
        for rid in set(rids):
            retained_rids |= self.expand_rid(rid)
        if len(retained_rids) > 0:
            self.log.debug("rids added from --rid %s: %s", ",".join(rids),
                           ",".join(retained_rids))
        return retained_rids

    def expand_subsets(self, subsets):
        """
        Parse the --subsets value and return the retained corresponding resource
        ids.
        """
        if subsets is None or self.options.subsets is None:
            return
        retained_rids = set()
        for resource in self.resources_by_id.values() + self.encap_resources.values():
            if resource.subset in subsets:
                retained_rids.add(resource.rid)
        if len(retained_rids) > 0:
            self.log.debug("rids added from --subsets %s: %s",
                           ",".join(subsets), ",".join(retained_rids))
        return retained_rids

    def expand_tags(self, tags):
        """
        Parse the --tags value and return the retained corresponding resource
        ids.
        ',' is interpreted as OR
        '+' is interpreted as AND
        '+' are evaluated before ','

        Example:
        --tags A,B : return rids of resource with either tag A or B
        --tags A+B : return rids of resource with both tags A and B
        --tags A+B,B+C : return rids of resource with either tags A and B
                         or tags B and C
        """
        if len(tags) == 0 or tags is None:
            return
        retained_rids = set()
        unions = []
        intersection = []
        for idx, tag in enumerate(tags):
            if tag[0] == "+":
                tag = tag[1:]
                intersection.append(tag)
                if idx == len(tags) - 1:
                    unions.append(intersection)
            else:
                if len(intersection) > 0:
                    # new intersection, store the current
                    unions.append(intersection)
                # open a new intersection
                intersection = [tag]
                if idx == len(tags) - 1:
                    unions.append(intersection)

        for intersection in unions:
            for resource in self.resources_by_id.values() + self.encap_resources.values():
                if set(intersection) & resource.tags == set(intersection):
                    retained_rids.add(resource.rid)
        if len(retained_rids) > 0:
            self.log.debug("rids added from --tags %s: %s", ",".join(tags),
                           ",".join(retained_rids))
        return retained_rids

    def standby_resources(self):
        """
        Return the list of resources flagged always on on this node
        """
        return [resource for resource in self.resources_by_id.values()
                if resource.standby]

    def prepare_options(self, action, options):
        """
        Return a Storage() from command line options or dict passed as
        <options>, sanitized, merge with default values in self.options.
        """
        if options is None:
            options = Storage()
        elif isinstance(options, dict):
            options = Storage(options)

        if is_string(options.slave):
            options.slave = options.slave.split(',')

        if isinstance(options.resource, list):
            for idx, resource in enumerate(options.resource):
                if not is_string(resource):
                    continue
                try:
                    options.resource[idx] = json.loads(resource)
                except ValueError:
                    raise ex.excError("invalid json in resource definition: "
                                      "%s" % options.resource[idx])

        self.options.update(options)
        options = self.options

        return options

    def options_to_rids(self, options, action):
        """
        Return the list of rids to apply an action to, from the command
        line options passed as <options>.
        """
        rid = options.get("rid", None)
        tags = options.get("tags", None)
        subsets = options.get("subsets", None)
        xtags = options.get("xtags", None)

        if rid is None:
            rid = []
        elif is_string(rid):
            rid = rid.split(',')

        if tags is None:
            tags = []
        elif is_string(tags):
            tags = tags.replace("+", ",+").split(',')

        if subsets is None:
            subsets = []
        elif is_string(subsets):
            subsets = subsets.split(',')

        if xtags is None:
            xtags = set()
        elif is_string(xtags):
            xtags = xtags.split(',')

        if len(self.resources_by_id.keys()) > 0:
            rids = set(self.all_rids())
            unsupported_rids = set(rid) - rids

            # --rid
            retained_rids = self.expand_rids(rid)
            if retained_rids is not None:
                rids &= retained_rids

            # --subsets
            retained_rids = self.expand_subsets(subsets)
            if retained_rids is not None:
                rids &= retained_rids

            # --tags
            retained_rids = self.expand_tags(tags)
            if retained_rids is not None:
                rids &= retained_rids

            # for delete, retain rids not in the built resources
            # (resources no longer supported)
            if action == "delete":
                rids |= unsupported_rids

            rids = list(rids)
            self.log.debug("rids retained after expansions intersection: %s",
                           ",".join(rids))

            if self.command_is_scoped(options) and len(rids) == 0:
                raise ex.excAbortAction("no resource match the given --rid, --subset "
                                        "and --tags specifiers")
        else:
            # no resources certainly mean the build was done with minimal=True
            # let the action go on. 'delete', for one, takes a --rid but does
            # not need resource initialization
            rids = rid

        return rids

    def action(self, action, options=None):
        self.allow_on_this_node(action)
        try:
            options = self.prepare_options(action, options)
            self.async_action(action)
        except ex.excError as exc:
            self.log.error(exc)
            return 1
        except ex.excAbortAction:
            return 0
        return self._action(action, options=options)

    @sched_action
    def _action(self, action, options=None):
        """
        Filter resources on which the service action must act.
        Abort if the service is frozen, or if --cluster is not set on a HA
        service.
        Set up the environment variables.
        Finally do the service action either in logged or unlogged mode.
        """

        try:
            self.action_rid_before_depends = self.options_to_rids(options, action)
        except ex.excAbortAction as exc:
            self.log.error(exc)
            return 1

        depends = set()
        for rid in self.action_rid_before_depends:
            depends |= self.action_rid_dependencies(action, rid) - set(self.action_rid_before_depends)

        if len(depends) > 0:
            self.log.info("add rid %s to satisfy dependencies" % ", ".join(depends))
            self.action_rid = list(set(self.action_rid_before_depends) | depends)
        else:
            self.action_rid = list(self.action_rid_before_depends)

        self.action_rid_depends = list(depends)

        self.action_start_date = datetime.datetime.now()

        if self.node is None:
            self.node = node.Node()

        if action not in ACTIONS_NO_STATUS_CHANGE and \
           'compliance' not in action and \
           'collector' not in action and \
            not options.dry_run and \
            not action.startswith("docker"):
            #
            # here we know we will run a resource state-changing action
            # purge the resource status file cache, so that we don't take
            # decision on outdated information
            #
            self.log.debug("purge all resource status file caches before "
                           "action %s", action)
            self.purge_status_caches()

        self.setup_environ(action=action)
        self.setup_signal_handlers()
        self.set_skip_resources(keeprid=self.action_rid, xtags=options.xtags)
        if action.startswith("print_") or \
           action.startswith("collector") or \
           action.startswith("json_"):
            return self.do_print_action(action, options)
        if action in ACTIONS_NO_LOG or \
           action.startswith("compliance") or \
           action.startswith("docker") or \
           options.dry_run:
            err = self.do_action(action, options)
        else:
            err = self.do_logged_action(action, options)
        return err

    def do_print_action(self, action, options):
        """
        Call the service method associated with action. This method produces
        data the caller will print.
        If --cluster is set, execute the action on remote nodes and
        aggregate the results.
        """
        _action = action + ""
        if action.startswith("json_"):
            action = "print_"+action[5:]
            self.node.options.format = "json"
            self.options.format = "json"
            options.format = "json"

        if "_json_" in action:
            action = action.replace("_json_", "_")
            self.node.options.format = "json"
            self.options.format = "json"
            options.format = "json"

        if options.cluster and options.format != "json":
            raise ex.excError("only the json output format is allowed with --cluster")

        if action.startswith("collector_"):
            from collector import Collector
            collector = Collector(options, self.node, self.svcname)
            func = getattr(collector, action)
        else:
            func = getattr(self, action)

        if not hasattr(func, "__call__"):
            raise ex.excError("%s is not callable" % action)

        psinfo = self.do_cluster_action(_action, collect=True, action_mode=False)

        try:
            data = func()
        except Exception as exc:
            data = {"error": str(exc)}

        if psinfo:
            # --cluster is set and we have remote responses
            results = self.join_cluster_action(**psinfo)
            for nodename in results:
                results[nodename] = results[nodename][0]
                if options.format == "json":
                    try:
                        results[nodename] = json.loads(results[nodename])
                    except (TypeError, ValueError) as exc:
                        results[nodename] = {"error": str(exc)}
            results[rcEnv.nodename] = data
            return results
        elif options.cluster:
            # no remote though --cluster is set
            results = {}
            results[rcEnv.nodename] = data
            return results

        return data

    def do_cluster_action(self, action, options=None, waitlock=60, collect=False, action_mode=True):
        """
        Execute an action on remote nodes if --cluster is set and the
        service is a flex, and this node is flex primary.

        edit config, validate config, and sync* are never executed through
        this method.

        If possible execute in parallel running subprocess. Aggregate and
        return results.
        """
        if options is None:
            options = self.options
        if not options.cluster:
            return

        if action in ("edit_config", "validate_config") or "sync" in action:
            return

        if self.topology == "flex":
            if rcEnv.nodename == self.drp_flex_primary:
                peers = set(self.drpnodes) - set([rcEnv.nodename])
            elif rcEnv.nodename == self.flex_primary:
                peers = set(self.nodes) - set([rcEnv.nodename])
            else:
                return
        elif not action_mode:
            if rcEnv.nodename in self.nodes:
                peers = set(self.nodes) | set(self.drpnodes)
            else:
                peers = set(self.drpnodes)
            peers -= set([rcEnv.nodename])
        else:
            return

        args = [arg for arg in sys.argv[1:] if arg not in ("-c", "--cluster")]
        if options.docker_argv and len(options.docker_argv) > 0:
            args += options.docker_argv

        def wrapper(queue, **kwargs):
            """
            Execute the remote action and enqueue or print results.
            """
            collect = kwargs["collect"]
            ret, out, err = self.remote_action(**kwargs)
            if collect:
                queue.put([out, err, ret])
            else:
                if len(out):
                    print(out)
                if len(err):
                    print(err)
            return out, err, ret

        if rcEnv.sysname == "Windows":
            parallel = False
        else:
            try:
                from multiprocessing import Process, Queue
                parallel = True
                results = None
                procs = {}
                queues = {}
            except ImportError:
                parallel = False
                results = {}
                procs = None
                queues = None

        for nodename in peers:
            kwargs = {
                "nodename": nodename,
                "action": " ".join(args),
                "waitlock": waitlock,
                "verbose": False,
                "sync": True,
                "action_mode": action_mode,
                "collect": True,
            }
            if parallel:
                queues[nodename] = Queue()
                proc = Process(target=wrapper, args=(queues[nodename],), kwargs=kwargs)
                proc.start()
                procs[nodename] = proc
            else:
                results[nodename] = wrapper(**kwargs)
        return {"procs": procs, "queues": queues, "results": results}

    @staticmethod
    def join_cluster_action(procs=None, queues=None, results=None):
        """
        Wait for subprocess to finish, aggregate and return results.
        """
        if procs is None or queues is None:
            return results
        results = {}
        joined = []
        while len(joined) < len(procs):
            for nodename, proc in procs.items():
                proc.join(0.1)
                if not proc.is_alive():
                    joined.append(nodename)
                queue = queues[nodename]
                if not queue.empty():
                    results[nodename] = queue.get()
        return results

    def do_action(self, action, options):
        """
        Acquire the service action lock, call the service action method,
        handles its errors, and finally release the lock.

        If --cluster is set, and the service is a flex, and we are
        flex_primary run the action on all remote nodes.
        """

        if action not in ACTIONS_NO_LOCK and self.topology not in TOPOLOGIES:
            raise ex.excError("invalid cluster type '%s'. allowed: %s" % (
                self.topology,
                ', '.join(TOPOLOGIES),
            ))

        err = 0
        waitlock = convert_duration(options.waitlock)
        if waitlock < 0:
            waitlock = self.lock_timeout

        if action == "sync_all" and self.command_is_scoped():
            for rid in self.action_rid:
                resource = self.resources_by_id[rid]
                if not resource.type.startswith("sync"):
                    continue
                try:
                    resource.reslock(action=action, suffix="sync")
                except ex.excError as exc:
                    self.log.error(str(exc))
                    return 1
        else:
            try:
                self.svclock(action, timeout=waitlock)
            except ex.excError as exc:
                self.log.error(str(exc))
                return 1

        psinfo = self.do_cluster_action(action, options=options)

        def call_action(action):
            self.action_triggers("pre", action)
            self.action_triggers("blocking_pre", action, blocking=True)
            err = getattr(self, action)()
            self.action_triggers("post", action)
            self.action_triggers("blocking_post", action, blocking=True)
            return err

        try:
            if action.startswith("compliance_"):
                err = getattr(self.compliance, action)()
            elif hasattr(self, action):
                self.running_action = action
                self.notify_action(action)
                err = call_action(action)
                if err is None:
                    err = 0
            else:
                self.log.error("unsupported action %s", action)
                err = 1
        except ex.excEndAction as exc:
            self.log.info(exc)
            err = 0
        except ex.excAbortAction as exc:
            msg = "'%s' action aborted by last resource" % action
            if len(str(exc)) > 0:
                msg += ": %s" % str(exc)
            self.log.info(msg)
            err = 0
        except ex.excError as exc:
            msg = "'%s' action stopped on execution error" % action
            self.log.debug(msg)
            msg = str(exc)
            if len(msg) > 0:
                self.log.error(msg)
            err = 1
            self.rollback_handler(action)
        except ex.excSignal:
            self.log.error("interrupted by signal")
            err = 1
        except:
            err = 1
            self.save_exc()
        finally:
            self.running_action = None
            if not (action == "delete" and not self.command_is_scoped()):
                self.update_status_data()
            self.clear_action(action, err)
            self.svcunlock()
            if action == "sync_all" and self.command_is_scoped():
                for rid in self.action_rid:
                    resource = self.resources_by_id[rid]
                    if not resource.type.startswith("sync"):
                        continue
                    resource.resunlock()

        if psinfo:
            self.join_cluster_action(**psinfo)

        return err

    def action_progress(self, action):
        progress = ACTION_ASYNC.get(action, {}).get("progress")
        if progress is None:
            return
        if action.startswith("sync"):
            progress = "syncing"
        return progress

    def notify_action(self, action):
        if os.environ.get("OSVC_ACTION_ORIGIN") == "daemon":
            return
        progress = self.action_progress(action)
        if progress is None:
            return
        local_expect = None
        if action in ("stop", "unprovision", "delete", "shutdown", "rollback") and not self.command_is_scoped():
            local_expect = "unset"
            if self.orchestrate == "ha":
                self.freeze()
        try:
            self.set_service_monitor(local_expect=local_expect, status=progress)
            self.log.debug("daemon notified of action '%s' begin" % action)
        except Exception as exc:
            self.log.warning("failed to notify action begin to the daemon: %s", str(exc))

    def clear_action(self, action, err):
        if os.environ.get("OSVC_ACTION_ORIGIN") == "daemon":
            return
        progress = self.action_progress(action)
        local_expect = None
        if progress is None:
            return
        if err:
            status = action + " failed"
        else:
            status = "idle"
            if action == "start":
                local_expect == "started"
        try:
            self.set_service_monitor(local_expect=local_expect, status=status)
            self.log.debug("daemon notified of action '%s' end" % action)
        except Exception as exc:
            self.log.warning("failed to notify action end to the daemon: %s", str(exc))

    def rollback_handler(self, action):
        """
        Call the rollback method if
        * the action triggering this handler is a start*
        * service is not configured to not disable rollback
        * --disable-rollback is not set
        * at least one resource has been flagged rollbackable during the
          start* action
        """
        if 'start' not in action:
            return
        if self.options.disable_rollback:
            self.log.info("skip rollback %s: as instructed by --disable-rollback", action)
            return
        if self.disable_rollback:
            self.log.info("skip rollback %s: as instructed by DEFAULT.rollback=false", action)
            return
        rids = [r.rid for r in self.get_resources() if r.can_rollback and not r.standby]
        if len(rids) == 0:
            self.log.info("skip rollback %s: no resource activated", action)
            return
        self.log.info("trying to rollback %s on %s", action, ', '.join(rids))
        try:
            self.rollback()
        except ex.excError:
            self.log.error("rollback %s failed", action)

    def do_logged_action(self, action, options):
        """
        Setup action logging to a machine-readable temp logfile, in preparation
        to the collector feeding.
        Do the action.
        Finally, feed the log to the collector.
        """
        import tempfile
        begin = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")

        # Provision a database entry to store action log later
        self.node.daemon_collector_xmlrpc("begin_action", self.svcname,
                                          action, self.node.agent_version(),
                                          begin, self.options.cron)

        # Per action logfile to push to database at the end of the action
        tmpfile = tempfile.NamedTemporaryFile(delete=False, dir=rcEnv.paths.pathtmp,
                                              prefix=self.svcname+'.'+action)
        actionlogfile = tmpfile.name
        tmpfile.close()
        fmt = "%(asctime)s;;%(name)s;;%(levelname)s;;%(message)s;;%(process)d;;EOL"
        actionlogformatter = logging.Formatter(fmt)
        actionlogfilehandler = logging.FileHandler(actionlogfile)
        actionlogfilehandler.setFormatter(actionlogformatter)
        actionlogfilehandler.setLevel(logging.INFO)
        self.log.addHandler(actionlogfilehandler)
        if "/svcmgr.py" in sys.argv:
            self.log.info(" ".join(sys.argv))

        err = self.do_action(action, options)

        # Push result and logs to database
        actionlogfilehandler.close()
        self.log.removeHandler(actionlogfilehandler)
        end = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        self.dblogger(action, begin, end, actionlogfile)
        return err

    def restart(self):
        """
        The 'restart' action entrypoint.
        This action translates into 'stop' followed by 'start'
        """
        self.stop()
        self.start()

    def _migrate(self):
        """
        Call the migrate action on all relevant resources.
        """
        rtypes = [
            "container.ovm",
            "container.hpvm",
            "container.esx",
            "container.lxd",
        ]
        self.sub_set_action(rtypes, "_migrate")

    def destination_node_sanity_checks(self, destination_node=None):
        """
        Raise an excError if
        * the destination node --to arg not set
        * the specified destination is the current node
        * the specified destination is not a service candidate node

        If the destination node is not specified and the cluster has
        only 2 nodes, consider the destination node is our peer.

        Return the validated destination node name.
        """
        if self.topology != "failover":
            raise ex.excError("this service topology is not 'failover'")
        if destination_node is None:
            destination_node = self.options.destination_node
        if destination_node is None and len(self.peers) == 2:
            nodenames = self.started_on()
            candidates = list(set(self.peers) - set(nodenames))
            if len(candidates) == 1:
                destination_node = candidates[0]
        if destination_node is None:
            raise ex.excError("a destination node must be provided this action")
        if destination_node == self.current_node():
            raise ex.excError("the destination is the source node")
        if destination_node not in self.nodes:
            raise ex.excError("the destination node %s is not in the service "
                              "nodes list" % destination_node)
        return destination_node

    @_master_action
    def migrate(self):
        """
        Service online migration.
        """
        dst = self.destination_node_sanity_checks()
        self.svcunlock()
        self._clear(nodename=rcEnv.nodename)
        self._clear(nodename=dst)
        self.daemon_mon_action("freeze", wait=True)
        src_node = self.current_node()
        self.daemon_service_action(["prstop"], nodename=src_node)
        try:
            self.daemon_service_action(["startfs", "--master"], nodename=dst)
            self._migrate()
        except:
            if len(self.get_resources('disk.scsireserv')) > 0:
                self.log.error("scsi reservations were dropped. you have to "
                               "acquire them now using the 'prstart' action "
                               "either on source node or destination node, "
                               "depending on your problem analysis.")
            raise
        self.daemon_service_action(["stop"], nodename=src_node)
        self.daemon_service_action(["prstart", "--master"], nodename=dst)

    def takeover(self):
        """
        Service move to local node.
        """
        self.destination_node_sanity_checks(rcEnv.nodename)
        self.svcunlock()
        self._clear(nodename=rcEnv.nodename)
        self._clear(nodename=self.options.destination_node)
        self.daemon_mon_action("stop", wait=True)
        self.daemon_service_action(["start"], nodename=rcEnv.nodename)
        self.daemon_mon_action("thaw", wait=True)

    def giveback(self):
        """
        Optimize service placement.
        """
        pass

    def scale(self):
        """
        Set the scale keyword.
        """
        if self.scale_target is None:
            raise ex.excError("can't scale: not a scaler")
        try:
            value = int(self.options.destination_node)
            assert value >= 0
        except Exception:
            raise ex.excError("invalid scale target: set '--to <n>' where n>=0")
        self._set("DEFAULT", "scale", str(value))
        self.set_service_monitor()

    def switch(self):
        """
        Service move to another node.
        """
        dst = self.destination_node_sanity_checks()
        self.svcunlock()
        self._clear(nodename=rcEnv.nodename)
        self._clear(nodename=dst)
        self.daemon_mon_action("stop", wait=True)
        self.daemon_service_action(["start"], nodename=dst, timeout=self.options.time)
        self.daemon_mon_action("thaw", wait=True)

    def collector_rest_get(self, *args, **kwargs):
        kwargs["svcname"] = self.svcname
        return self.node.collector_rest_get(*args, **kwargs)

    def collector_rest_post(self, *args, **kwargs):
        kwargs["svcname"] = self.svcname
        return self.node.collector_rest_post(*args, **kwargs)

    def collector_rest_put(self, *args, **kwargs):
        kwargs["svcname"] = self.svcname
        return self.node.collector_rest_put(*args, **kwargs)

    def collector_rest_delete(self, *args, **kwargs):
        kwargs["svcname"] = self.svcname
        return self.node.collector_rest_delete(*args, **kwargs)

    def write_config(self):
        """
        Rewrite the service configuration file, using the current parser
        object in self.config write method.
        Also reset the file mode to 644.
        """
        import tempfile
        import shutil
        try:
            tmpfile = tempfile.NamedTemporaryFile()
            fname = tmpfile.name
            tmpfile.close()
            with open(fname, "w") as tmpfile:
                self.config.write(tmpfile)
                tmpfile.flush()
            shutil.move(fname, self.paths.cf)
        except (OSError, IOError) as exc:
            print("failed to write new %s (%s)" % (self.paths.cf, str(exc)),
                  file=sys.stderr)
            raise ex.excError()
        try:
            os.chmod(self.paths.cf, 0o0644)
        except (OSError, IOError) as exc:
            self.log.debug("failed to set %s mode: %s", self.paths.cf, str(exc))

    @lazy
    def config(self):
        """
        Initialize the service configuration parser object. Using an
        OrderDict type to preserve the options and sections ordering,
        if possible.

        The parser object is a opensvc-specified class derived from
        optparse.RawConfigParser.
        """
        return read_cf(self.paths.cf)

    def setenv(self, args, interactive=False):
        """
        For each option in the 'env' section of the configuration file,
        * rewrite the value using the value specified in a corresponding
          --env <option>=<value> commandline arg
        * or prompt for the value if --interactive is set, and rewrite
        * or leave the value as is, considering the default is accepted
        """
        explicit_options = []

        for arg in args:
            idx = arg.index("=")
            option = arg[:idx]
            value = arg[idx+1:]
            self._set("env", option, value)
            explicit_options.append(option)

        if not interactive:
            return

        if not os.isatty(0):
            raise ex.excError("--interactive is set but input fd is not a tty")

        def get_href(ref):
            ref = ref.strip("[]")
            try:
                response = node.urlopen(ref)
                return response.read()
            except:
                return ""

        def print_comment(comment):
            """
            Print a env keyword comment. For use in the interactive service
            create codepath.
            """
            import re
            comment = re.sub("(\[.+://.+])", lambda m: get_href(m.group(1)), comment)
            print(comment)

        for key, default_val in self.env_section_keys().items():
            if key.endswith(".comment"):
                continue
            if key in explicit_options:
                continue
            if self.config.has_option("env", key+".comment"):
                print_comment(self.config.get("env", key+".comment"))
            newval = raw_input("%s [%s] > " % (key, str(default_val)))
            if newval != "":
                self._set("env", key, newval)

    def set_disable(self, rids=None, disable=True):
        """
        Set the disable to <disable> (True|False) in the configuration file,
        * at DEFAULT level if no resources were specified
        * in each resource section if resources were specified
        """
        lines = self._read_cf().splitlines()

        if rids is None:
            rids = []

        if not self.command_is_scoped() and \
           (len(rids) == 0 or len(rids) == len(self.resources_by_id)):
            rids = ['DEFAULT']

        for rid in rids:
            if rid != 'DEFAULT' and not self.config.has_section(rid):
                self.log.error("service %s has no resource %s", self.svcname, rid)
                continue

            if disable:
                self.log.info("set %s.disable = true", rid)
                lines = self.set_line(lines, rid, "disable", "true")
            elif self.config.has_option(rid, "disable"):
                self.log.info("remove %s.disable", rid)
                lines = self.unset_line(lines, rid, "disable")

            #
            # if we set <section>.disable = <bool>,
            # remove <section>.disable@<scope> = <not bool>
            #
            if rid == "DEFAULT":
                items = self.config.defaults().items()
            else:
                items = self.config.items(rid)
            for option, value in items:
                if not option.startswith("disable@"):
                    continue
                if value == True:
                    continue
                self.log.info("remove %s.%s = false", rid, option)
                lines = self.unset_line(lines, rid, option)


        self.unset_lazy("disabled")

        # update the resource objects disable prop
        for rid in rids:
            if rid == "DEFAULT":
                continue
            resource = self.get_resource(rid)
            if resource is None:
                continue
            if disable:
                resource.disable()
            else:
                resource.enable()

        try:
            self._write_cf(lines)
        except (IOError, OSError) as exc:
            raise ex.excError(str(exc))
        self.unset_lazy("config")

    def enable(self):
        """
        The 'enable' action entrypoint.
        """
        return self.set_disable(self.action_rid, False)

    def disable(self):
        """
        The 'disable' action entrypoint.
        """
        return self.set_disable(self.action_rid, True)

    def delete_service_logs(self):
        """
        Delete the service configuration logs
        """
        import glob
        patterns = [
            os.path.join(rcEnv.paths.pathlog, self.svcname+".log*"),
            os.path.join(rcEnv.paths.pathlog, self.svcname+".debug.log*"),
            os.path.join(rcEnv.paths.pathlog, '.'+self.svcname+".log*"),
            os.path.join(rcEnv.paths.pathlog, '.'+self.svcname+".debug.log*"),
            os.path.join(self.var_d, "frozen"),
        ]
        for pattern in patterns:
            for fpath in glob.glob(pattern):
                self.log.info("remove %s", fpath)
                os.unlink(fpath)

    def delete_service_conf(self):
        """
        Delete the service configuration files
        """
        import shutil
        dpaths = [
            os.path.join(rcEnv.paths.pathetc, self.svcname+".dir"),
            os.path.join(rcEnv.paths.pathetc, self.svcname+".d"),
            os.path.join(self.var_d),
        ]
        fpaths = [
            self.paths.cf,
            os.path.join(rcEnv.paths.pathetc, self.svcname),
            os.path.join(rcEnv.paths.pathetc, self.svcname+".d"),
        ]
        for fpath in fpaths:
            if os.path.exists(fpath) and \
               (os.path.islink(fpath) or os.path.isfile(fpath)):
                self.log.info("remove %s", fpath)
                os.unlink(fpath)
        for dpath in dpaths:
            if os.path.exists(dpath):
                self.log.info("remove %s", dpath)
                shutil.rmtree(dpath)

    def delete_resources(self, rids=None):
        """
        Delete service resources objects and references and in configuration file
        """
        if rids is None:
            rids = self.action_rid
        self._delete_resources_config(rids)
        self._delete_resources_live(rids)

    def _delete_resources_live(self, rids):
        """
        Delete service resources objects and references
        """
        for rid in rids:
            self._delete_resource_live(rid)

    def _delete_resource_live(self, rid):
        """
        Delete service a resource object and references
        """
        if rid in self.resources_by_id:
            del(self.resources_by_id[rid])
        if rid in self.encap_resources:
            del(self.encap_resources[rid])
        rs_to_delete = []
        for rset_id, rset in self.resourcesets_by_id.items():
            to_delete = []
            for idx, res in enumerate(rset.resources):
                if res.rid == rid:
                    res.remove_is_provisioned_flag()
                    to_delete.append(idx)
            for idx in to_delete:
                del rset.resources[idx]
            if len(rset.resources) == 0:
                rs_to_delete.append(rset_id)
        for rset_id in rs_to_delete:
            del self.resourcesets_by_id[rset_id]

    def _delete_resources_config(self, rids):
        """
        Delete service resources from its configuration file
        """
        lines = self._read_cf().splitlines()
        need_write = False

        for rid in rids:
            section = "[%s]" % rid
            in_section = False
            for i, line in enumerate(lines):
                sline = line.strip()
                if sline == section:
                    in_section = True
                    need_write = True
                    del lines[i]
                    while i < len(lines) and not lines[i].strip().startswith("["):
                        del lines[i]

            if not in_section:
                print("service", self.svcname, "has no resource", rid, file=sys.stderr)

        if not need_write:
            return

        buff = "\n".join(lines)

        try:
            self._write_cf(buff)
        except (IOError, OSError):
            raise ex.excError("failed to rewrite %s" % self.paths.cf)

        self.unset_lazy("config")

    def delete(self):
        """
        The 'delete' action entrypoint.
        If --unprovision is set, call the unprovision method.
        Then if no resource specifier is set, remove all service files in
        <pathetc>.
        If a resource specifier is set, only delete the corresponding
        sections in the configuration file.
        """
        if self.options.unprovision:
            self.unprovision()

        if not self.command_is_scoped():
            if os.environ.get("OSVC_ACTION_ORIGIN") != "daemon":
                # the daemon only delete the whole service, so no
                # need to remove this node from the nodes list of
                # remote instances
                if rcEnv.nodename in self.nodes:
                    self.set_multi([
                       "nodes="+rcEnv.nodename,
                       "drpnodes=",
                    ])
                elif rcEnv.nodename in self.drpnodes:
                    self.set_multi([
                       "drpnodes="+rcEnv.nodename,
                       "nodes=",
                    ])
                self.svcunlock()
                for peer in self.peers:
                    if peer == rcEnv.nodename:
                        continue
                    self.daemon_service_action([
                        "set",
                        "--kw", "nodes-=" + rcEnv.nodename,
                        "--kw", "drpnodes-=" + rcEnv.nodename,
                    ], nodename=peer, sync=False)
            self.delete_service_conf()
            self.delete_service_logs()
        else:
            self.delete_resources()

    def docker(self):
        """
        The 'docker' action entry point.
        Parse the docker argv and substitute known patterns before relaying
        the argv to the docker command.
        Set the socket to point the service-private docker daemon if
        the service has such a daemon.
        """
        import subprocess
        containers = self.get_resources('container.docker')
        if self.options.docker_argv is None:
            print("no docker command arguments supplied", file=sys.stderr)
            return 1

        def subst(argv):
            """
            Parse the docker argv and substitute known patterns.
            """
            import re
            for idx, arg in enumerate(argv):
                if arg in ("%instances%", "{instances}"):
                    del argv[idx]
                    instances = [resource.container_name for resource in containers
                                 if not resource.skip and not resource.is_disabled()]
                    for instance in instances:
                        argv.insert(idx, instance)
            for idx, arg in enumerate(argv):
                if arg in ("%images%", "{images}"):
                    del argv[idx]
                    images = list(set([resource.run_image for resource in containers
                                       if not resource.skip and not resource.is_disabled()]))
                    for image in images:
                        argv.insert(idx, image)
            for idx, arg in enumerate(argv):
                if arg in ("%as_service%", "{as_service}"):
                    del argv[idx]
                    argv[idx:idx] = ["-u", self.svcname+"@"+rcEnv.nodename]
                    argv[idx:idx] = ["-p", self.node.config.get("node", "uuid")]
                    if self.dockerlib.docker_min_version("1.12"):
                        pass
                    elif self.dockerlib.docker_min_version("1.10"):
                        argv[idx:idx] = ["--email", self.svcname+"@"+rcEnv.nodename]
            for idx, arg in enumerate(argv):
                if re.match(r'\{container#\w+\}', arg):
                    container_rid = arg.strip("{}")
                    if container_rid not in self.resources_by_id:
                        continue
                    container = self.resources_by_id[container_rid]
                    if container.docker_service:
                        name = container.service_name
                    else:
                        name = container.container_name
                    del argv[idx]
                    argv.insert(idx, name)
            return argv

        if len(containers) == 0:
            print("this service has no docker resource", file=sys.stderr)
            return 1

        self.dockerlib.docker_start(verbose=False)
        cmd = self.dockerlib.docker_cmd + subst(self.options.docker_argv)
        proc = subprocess.Popen(cmd)
        proc.communicate()
        return proc.returncode

    def freezestop(self):
        """
        The freezestop monitor action.
        """
        self.freeze()
        self.stop()

    def freeze(self):
        """
        Set the frozen flag.
        """
        self.master_freeze()
        self.slave_freeze()

    @_master_action
    def master_freeze(self):
        self.freezer.freeze()

    @_slave_action
    def slave_freeze(self):
        self.encap_cmd(['freeze'], verbose=True)

    def thaw(self):
        """
        Unset the frozen flag.
        """
        self.master_thaw()
        self.slave_thaw()

    @_master_action
    def master_thaw(self):
        self.freezer.thaw()

    @_slave_action
    def slave_thaw(self):
        self.encap_cmd(['thaw'], verbose=True)

    def frozen(self):
        """
        Return True if the service is frozen.
        """
        return self.freezer.frozen(strict=True)

    def pull(self):
        """
        Pull a service configuration from the collector, installs it and
        create the svcmgr link.
        """
        data = self.node.collector_rest_get("/services/"+self.svcname+"?props=svc_config&meta=0")
        if "error" in data:
            raise ex.excError(data["error"])
        if len(data["data"]) == 0:
            raise ex.excError("service not found on the collector")
        if data["data"][0]["svc_config"] is None:
            raise ex.excError("service has an empty configuration on the collector")
        if len(data["data"][0]["svc_config"]) == 0:
            raise ex.excError("service has an empty configuration on the collector")
        buff = data["data"][0]["svc_config"].replace("\\n", "\n").replace("\\t", "\t")
        import codecs
        with codecs.open(self.paths.cf, "w", "utf8") as ofile:
            ofile.write(buff)
            ofile.flush()
        self.log.info("%s pulled", self.paths.cf)
        self.node.install_service_files(self.svcname)

        if self.options.provision:
            self.action("provision")

    def save_exc(self):
        """
        A helper method to save stacks in the service log.
        """
        self.log.error("unexpected error. stack saved in the service debug log")
        import traceback
        buff = traceback.format_exc()
        for line in buff.splitlines():
            self.log.debug(line)

    def vcall(self, *args, **kwargs):
        """
        Wrap vcall, setting the service logger
        """
        kwargs["log"] = self.log
        return vcall(*args, **kwargs)

    def lcall(self, *args, **kwargs):
        """
        Wrap lcall, setting the service logger
        """
        kwargs["logger"] = self.log
        return lcall(*args, **kwargs)

    def allocate_rid(self, group, sections):
        """
        Return an unused rid in <group>.
        """
        prefix = group + "#"
        rids = [section for section in sections if section.startswith(prefix)]
        idx = 1
        while True:
            rid = "#".join((group, str(idx)))
            if rid in rids:
                idx += 1
                continue
            return rid

    def update(self):
        """
        The 'update' action entry point.
        Add resources to the service configuration, and provision them if
        instructed to do so.
        """
        from keywords import MissKeyNoDefault, KeyInvalidValue

        sections = {}
        rtypes = {}
        defaults = self.config.defaults()
        for section in self.config.sections():
            sections[section] = {}
            elements = section.split('#')
            if len(elements) == 2:
                rtype = elements[0]
                ridx = elements[1]
                if rtype not in rtypes:
                    rtypes[rtype] = set()
                rtypes[rtype].add(ridx)
            for option, value in self.config.items(section):
                if option in list(defaults.keys()) + ['rtype']:
                    continue
                sections[section][option] = value

        import svcBuilder

        rid = []

        for data in self.options.resource:
            is_resource = False
            if 'rid' in data:
                section = data['rid']
                if '#' not in section:
                    raise ex.excError("%s must be formatted as 'rtype#n'" % section)
                elements = section.split('#')
                if len(elements) != 2:
                    raise ex.excError("%s must be formatted as 'rtype#n'" % section)
                del data['rid']
                if section in sections:
                    sections[section].update(data)
                else:
                    sections[section] = data
                is_resource = True
            elif 'rtype' in data and data["rtype"] == "env":
                del data["rtype"]
                if "env" in sections:
                    sections["env"].update(data)
                else:
                    sections["env"] = data
            elif 'rtype' in data and data["rtype"] != "DEFAULT":
                section = self.allocate_rid(data['rtype'], sections)
                self.log.info("allocated rid %s" % section)
                del data['rtype']
                sections[section] = data
                is_resource = True
            else:
                if "rtype" in data:
                    del data["rtype"]
                defaults.update(data)

            if is_resource:
                try:
                    sections[section].update(self.kwdict.KEYS.update(section, data))
                except (MissKeyNoDefault, KeyInvalidValue) as exc:
                    if not self.options.interactive:
                        raise ex.excError(str(exc))
                rid.append(section)

        for section, data in sections.items():
            if not self.config.has_section(section):
                self.config.add_section(section)
            for key, val in data.items():
                self.config.set(section, key, val)

        self.write_config()

        for section in rid:
            group = section.split("#")[0]
            svcBuilder.add_resource(self, group, section)

        if self.options.provision and len(rid) > 0:
            options = Storage(self.options)
            options.rid = rid
            self.action("provision", options)

    def allow_on_this_node(self, action):
        """
        Raise excError if the service is not allowed to run on this node.
        In other words, the nodename is not a service node or drpnode, nor the
        service mode is cloud proxy.
        """
        if action in ACTIONS_ALLOW_ON_INVALID_NODE:
            return
        if self.svc_env != 'PRD' and rcEnv.node_env == 'PRD':
            raise ex.excError('not allowed to run on this node (svc env=%s node env=%s)' % (self.svc_env, rcEnv.node_env))
        if self.type in rcEnv.vt_cloud:
            return
        if rcEnv.nodename in self.nodes:
            return
        if rcEnv.nodename in self.drpnodes:
            return
        raise ex.excError("action '%s' aborted because this node's hostname "
                          "'%s' is not a member of DEFAULT.nodes, "
                          "DEFAULT.drpnode nor DEFAULT.drpnodes" % \
                          (action, rcEnv.nodename))

    def logs(self, nodename=None):
        try:
            self._logs(nodename=nodename)
        except ex.excSignal:
            return
        except (OSError, IOError) as exc:
            if exc.errno == 32:
                # broken pipe
                return

    def _logs(self, nodename=None):
        if nodename is None:
            nodename = self.options.node
        if self.options.local:
            nodes = [rcEnv.nodename]
        elif self.options.node:
            nodes = [self.options.node]
        else:
            nodes = self.peers
        from rcColor import colorize_log_line
        lines = []
        for nodename in nodes:
            lines += self.daemon_backlogs(nodename)
            for line in sorted(lines):
                line = colorize_log_line(line)
                if line:
                    print(line)
        if not self.options.follow:
            return
        for line in self.daemon_logs(nodes):
            line = colorize_log_line(line)
            if line:
                print(line)

    def placement_optimal(self, data=None):
        if data is None:
            data = self.node._daemon_status()
        placement = data.get("monitor").get("services").get(self.svcname).get("placement")
        if placement == "optimal":
            return True
        return False

    #########################################################################
    #
    # daemon communications
    #
    #########################################################################
    def daemon_backlogs(self, nodename):
        options = {
            "svcname": self.svcname,
            "backlog": self.options.backlog,
            "debug": self.options.debug,
        }
        for lines in self.daemon_get_stream(
            {"action": "service_logs", "options": options},
            nodename=nodename,
        ):
            if lines is None:
                break
            for line in lines:
                yield line

    def daemon_logs(self, nodes=None):
        options = {
            "svcname": self.svcname,
            "backlog": 0,
        }
        for lines in self.daemon_get_streams(
            {"action": "service_logs", "options": options},
            nodenames=nodes,
        ):
            if lines is None:
                break
            for line in lines:
                yield line

    def abort(self):
        pass

    def clear(self):
        if self.options.local:
           self._clear()
        elif self.options.node:
           self._clear(self.options.node)
        else:
           cleared = 0
           for nodename in self.peers:
               try:
                   self._clear(nodename)
               except ex.excError as exc:
                   self.log.warning(exc)
                   continue
               cleared += 1
           if cleared < len(self.peers):
               raise ex.excError("cleared on %d/%d nodes" % (cleared, len(self.peers)))

    def _clear(self, nodename=None):
        options = {
            "svcname": self.svcname,
        }
        data = self.daemon_send(
            {"action": "clear", "options": options},
            nodename=nodename,
        )
        if data is None or data["status"] != 0:
            raise ex.excError("clear on node %s failed" % nodename)

    def wake_monitor(self):
        options = {
            "svcname": self.svcname,
        }
        try:
            data = self.daemon_send(
                {"action": "wake_monitor", "options": options},
                nodename=self.options.node,
                silent=True,
            )
            if data and data["status"] != 0:
                self.log.warning("wake monitor failed")
        except Exception as exc:
            self.log.warning("wake monitor failed: %s", str(exc))

    def set_service_monitor(self, status=None, local_expect=None, global_expect=None, stonith=None, svcname=None):
        if svcname is None:
            svcname = self.svcname
        options = {
            "svcname": svcname,
            "status": status,
            "local_expect": local_expect,
            "global_expect": global_expect,
            "stonith": stonith,
        }
        try:
            data = self.daemon_send(
                {"action": "set_service_monitor", "options": options},
                nodename=self.options.node,
                silent=True,
            )
            if data and data["status"] != 0:
                self.log.warning("set monitor status failed")
        except Exception as exc:
            self.log.warning("set monitor status failed: %s", str(exc))

    def daemon_service_action(self, cmd, nodename=None, sync=True, timeout=0, collect=False, action_mode=True):
        """
        Execute a service action on a peer node.
        If sync is set, wait for the action result.
        """
        if timeout is not None:
            timeout = convert_duration(timeout)
        if nodename is None:
            nodename = self.options.node
        if nodename not in self.node.cluster_nodes:
            try:
                secret = self.node.conf_get("cluster", "secret", impersonate=nodename)
            except:
                raise ex.excError("unknown cluster secret to communicate with node %s" % nodename)
            try:
                cluster_name = self.node.conf_get("cluster", "name", impersonate=nodename)
            except:
                raise ex.excError("unknown cluster name to communicate with node %s" % nodename)
        else:
            secret = self.cluster_key
            cluster_name = None


        options = {
            "svcname": self.svcname,
            "cmd": cmd,
            "sync": sync,
            "action_mode": action_mode,
        }
        if action_mode:
            self.log.info("request action '%s' on node %s", " ".join(cmd), nodename)
        try:
            data = self.daemon_send(
                {"action": "service_action", "options": options},
                nodename=nodename,
                silent=True,
                timeout=timeout,
                secret=secret,
                cluster_name=cluster_name,
            )
        except Exception as exc:
            self.log.error("post service action on node %s failed: %s",
                           nodename, exc)
            return 1
        if data is None or data["status"] != 0:
            self.log.error("post service action on node %s failed",
                           nodename)
            return 1
        if "data" not in data:
            return 0
        data = data["data"]
        if collect:
            return data["ret"], data.get("out", ""), data.get("err", "")
        else:
            if data.get("out") and len(data["out"]) > 0:
                for line in data["out"].splitlines():
                   print(line)
            if data.get("err") and len(data["err"]) > 0:
                for line in data["err"].splitlines():
                   print(line, file=sys.stderr)
            return data["ret"]

    def get_pg_settings(self, s):
        d = {}
        options = (
            "cpus",
            "cpu_shares",
            "cpu_quota",
            "mems",
            "mem_oom_control",
            "mem_limit",
            "mem_swappiness",
            "vmem_limit",
            "blkio_weight",
        )

        for option in options:
            try:
                d[option] = self.conf_get(s, "pg_"+option)
            except ex.OptNotFound as exc:
                pass

        return d

    @lazy
    def pg_settings(self):
        return self.get_pg_settings("DEFAULT")

    @lazy
    def slave_num(self):
        try:
            return int(self.svcname.split(".")[0])
        except ValueError:
            return 0

    def unset_lazy(self, prop):
        """
        Expose the unset_lazy(self, ...) utility function as a method,
        so Node() users don't have to import it from rcUtilities.
        """
        unset_lazy(self, prop)

  0707010005ca10000081b40000000000000000000000015ab2175400010576000000660000000000000000000000000000002900000000root/usr/share/opensvc/lib/svcBuilder.py  from __future__ import print_function
import os
import sys
import re
import glob

from rcGlobalEnv import rcEnv, Storage
from rcNode import discover_node
import rcLogger
import resSyncRsync
import rcExceptions as ex
import rcConfigParser
from rcUtilities import cmdline2list, ximport, check_privs

if 'PATH' not in os.environ:
    os.environ['PATH'] = ""
os.environ['LANG'] = 'C'
os.environ['PATH'] += ':/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin'

def get_tags(svc, section):
    try:
        s = svc.conf_get(section, 'tags')
    except ex.OptNotFound as exc:
        s = exc.default
    return s

def get_optional(svc, section):
    try:
        return svc.conf_get(section, "optional")
    except ex.OptNotFound:
        return None

def get_monitor(svc, section):
    try:
        return svc.conf_get(section, "monitor")
    except ex.OptNotFound as exc:
        return exc.default

def get_provision(svc, section):
    try:
        return svc.conf_get(section, "provision")
    except ex.OptNotFound as exc:
        return exc.default

def get_unprovision(svc, section):
    try:
        return svc.conf_get(section, "unprovision")
    except ex.OptNotFound as exc:
        return exc.default

def get_encap(svc, section):
    try:
        return svc.conf_get(section, "encap")
    except ex.OptNotFound as exc:
        return exc.default

def get_shared(svc, section):
    try:
        return svc.conf_get(section, "shared")
    except ex.OptNotFound as exc:
        return exc.default

def get_rcmd(svc, section):
    try:
        return svc.conf_get(section, 'rcmd')
    except ex.OptNotFound as exc:
        return exc.default

def get_subset(svc, section):
    try:
        return svc.conf_get(section, 'subset')
    except ex.OptNotFound as exc:
        return exc.default

def get_osvc_root_path(svc, section):
    try:
        return svc.conf_get(section, 'osvc_root_path')
    except ex.OptNotFound as exc:
        return exc.default

def get_restart(svc, section):
    try:
        return svc.conf_get(section, 'restart')
    except ex.OptNotFound as exc:
        return exc.default

def get_disabled(svc, section):
    try:
        return svc.conf_get(section, 'disable')
    except ex.OptNotFound as exc:
        return exc.default

def get_promote_rw(svc, section):
    try:
        return svc.conf_get(section, 'promote_rw')
    except ValueError as exc:
        return False
    except ex.OptNotFound as exc:
        return exc.default

def get_standby(svc, section):
    try:
        return svc.conf_get(section, 'standby')
    except ex.OptNotFound as exc:
        # backward compat with always_on
        try:
            return standby_from_always_on(svc, section)
        except ex.OptNotFound:
            pass
        return exc.default

def init_kwargs(svc, s):
    return {
        "rid": s,
        "subset": get_subset(svc, s),
        "tags": get_tags(svc, s),
        "standby": get_standby(svc, s),
        "disabled": get_disabled(svc, s),
        "optional": get_optional(svc, s),
        "monitor": get_monitor(svc, s),
        "skip_provision": not get_provision(svc, s),
        "skip_unprovision": not get_unprovision(svc, s),
        "restart": get_restart(svc, s),
        "shared": get_shared(svc, s),
        "encap": get_encap(svc, s),
        "promote_rw": get_promote_rw(svc, s),
    }

def standby_from_always_on(svc, section):
    always_on_opt = svc.conf_get(section, "always_on")
    if rcEnv.nodename in always_on_opt:
        return True
    if 'nodes' in always_on_opt and rcEnv.nodename in svc.nodes:
        return True
    if 'drpnodes' in always_on_opt and rcEnv.nodename in svc.drpnodes:
        return True
    return False

def get_sync_args(svc, s):
    kwargs = {}
    defaults = svc.config.defaults()

    if svc.config.has_option(s, 'sync_max_delay'):
        kwargs['sync_max_delay'] = svc.conf_get(s, 'sync_max_delay')
    elif 'sync_max_delay' in defaults:
        kwargs['sync_max_delay'] = svc.conf_get('DEFAULT', 'sync_max_delay')

    if svc.config.has_option(s, 'schedule'):
        kwargs['schedule'] = svc.conf_get(s, 'schedule')
    elif svc.config.has_option(s, 'period') or svc.config.has_option(s, 'sync_period'):
        # old schedule syntax compatibility
        from rcScheduler import Scheduler
        kwargs['schedule'] = Scheduler().sched_convert_to_schedule(svc.config, s, prefix='sync_')
    elif 'sync_schedule' in defaults:
        kwargs['schedule'] = svc.conf_get('DEFAULT', 'sync_schedule')
    elif 'sync_period' in defaults:
        # old schedule syntax compatibility for internal sync
        from rcScheduler import Scheduler
        kwargs['schedule'] = Scheduler().sched_convert_to_schedule(svc.config, s, prefix='sync_')

    return kwargs

def add_resources(svc, restype):
    for s in svc.config.sections():
        try:
            add_resource(svc, restype, s)
        except ex.RequiredOptNotFound:
            continue

def add_resource(svc, restype, s):
    if restype == "pool":
        restype = "zpool"
        match = "[z]{0,1}pool#"
    else:
        match = restype+"#"

    if restype in ("disk", "vg", "zpool") and re.match(match+'.+pr', s, re.I) is not None:
        # persistent reserv resource are declared by their peer resource:
        # don't add them from here
        return

    if s != 'app' and s != restype and re.match(match, s, re.I) is None:
        return

    tags = get_tags(svc, s)
    encap = get_encap(svc, s)

    if svc.encap and 'encap' not in tags and not encap:
        return

    if not svc.encap and (encap or 'encap' in tags):
        svc.has_encap_resources = True
        try:
            subset = svc.conf_get(s, 'subset')
        except ex.OptNotFound as exc:
            subset = exc.default
        svc.encap_resources[s] = Storage({
            "rid": s,
            "tags": tags,
            "encap": encap,
            "subset": subset,
        })
        return

    if s in svc.resources_by_id:
        return

    globals()['add_'+restype](svc, s)

def add_ip_gce(svc, s):
    kwargs = init_kwargs(svc, s)

    try:
        rtype = svc.conf_get(s, 'type')
    except ex.OptNotFound as exc:
        rtype = exc.default

    if rtype != "gce":
        return

    try:
         kwargs['ipname'] = svc.conf_get(s, 'ipname')
    except ex.OptNotFound:
         pass
    kwargs['ipdev'] = svc.conf_get(s, 'ipdev')

    try:
        kwargs['routename'] = svc.conf_get(s, 'routename')
    except ex.OptNotFound as exc:
        kwargs['routename'] = exc.default

    try:
        kwargs['gce_zone'] = svc.conf_get(s, 'gce_zone')
    except ex.OptNotFound as exc:
        kwargs['gce_zone'] = exc.default

    ip = __import__('resIpGce')
    r = ip.Ip(**kwargs)
    svc += r

def add_ip_amazon(svc, s):
    kwargs = init_kwargs(svc, s)

    try:
        rtype = svc.conf_get(s, 'type')
    except ex.OptNotFound as exc:
        rtype = exc.default

    if rtype != "amazon":
        return

    try:
        kwargs['ipname'] = svc.conf_get(s, 'ipname')
    except ex.OptNotFound:
         pass
    kwargs['ipdev'] = svc.conf_get(s, 'ipdev')

    try:
        kwargs['eip'] = svc.conf_get(s, 'eip')
    except ex.OptNotFound as exc:
        kwargs['eip'] = None

    ip = __import__('resIpAmazon')
    r = ip.Ip(**kwargs)
    svc += r

def add_ip(svc, s):
    """Parse the configuration file and add an ip object for each [ip#n]
    section. Ip objects are stored in a list in the service object.
    """
    try:
        rtype = svc.conf_get(s, 'type')
    except ex.OptNotFound as exc:
        rtype = exc.default

    if rtype == "amazon":
        return add_ip_amazon(svc, s)
    elif rtype == "gce":
        return add_ip_gce(svc, s)

    kwargs = init_kwargs(svc, s)

    try:
        kwargs['ipname'] = svc.conf_get(s, 'ipname')
    except ex.OptNotFound:
        pass

    kwargs['ipdev'] = svc.conf_get(s, 'ipdev')

    try:
        kwargs['mask'] = svc.conf_get(s, 'netmask')
    except ex.OptNotFound as exc:
        kwargs['mask'] = exc.default

    try:
        kwargs['gateway'] = svc.conf_get(s, 'gateway')
    except ex.OptNotFound as exc:
        kwargs['gateway'] = exc.default

    try:
        zone = svc.conf_get(s, 'zone')
    except ex.OptNotFound as exc:
        zone = exc.default

    if rtype == "docker":
        try:
            kwargs['container_rid'] = svc.conf_get(s, 'container_rid')
        except ex.OptNotFound as exc:
            kwargs['container_rid'] = exc.default
        try:
            kwargs['network'] = svc.conf_get(s, 'network')
        except ex.OptNotFound as exc:
            kwargs['network'] = exc.default
        try:
            kwargs['del_net_route'] = svc.conf_get(s, 'del_net_route')
        except ex.OptNotFound as exc:
            kwargs['del_net_route'] = exc.default

    if rtype == "cni":
        try:
            kwargs['network'] = svc.conf_get(s, 'network')
        except ex.OptNotFound as exc:
            kwargs['network'] = exc.default
        try:
            kwargs['container_rid'] = svc.conf_get(s, 'container_rid')
        except ex.OptNotFound as exc:
            pass
        try:
            kwargs['expose'] = svc.conf_get(s, 'expose')
        except ex.OptNotFound as exc:
            kwargs['expose'] = exc.default

    if rtype == "crossbow":
        try:
            kwargs['ipdevExt'] = svc.conf_get(s, 'ipdevext')
        except ex.OptNotFound as exc:
            kwargs['ipdevExt'] = exc.default
        if zone is not None:
            svc.log.error("'zone' and 'type=crossbow' are incompatible in section %s"%s)
            return
        ip = __import__('resIpCrossbow')
    elif zone is not None:
        kwargs['zone'] = zone
        ip = __import__('resIpZone')
    elif rtype == "docker":
        ip = __import__('resIpDocker'+rcEnv.sysname)
    elif rtype == "cni":
        ip = __import__('resIpCni')
    else:
        ip = __import__('resIp'+rcEnv.sysname)

    r = ip.Ip(**kwargs)
    svc += r

def add_lv(svc, s):
    kwargs = init_kwargs(svc, s)
    kwargs['name'] = svc.conf_get(s, 'name')
    kwargs['vg'] = svc.conf_get(s, 'vg')
    try:
        m = __import__('resDiskLv'+rcEnv.sysname)
    except ImportError:
        svc.log.error("resDiskLv%s is not implemented"%rcEnv.sysname)
        return
    r = m.Disk(**kwargs)
    svc += r

def add_md(svc, s):
    kwargs = init_kwargs(svc, s)
    try:
        kwargs['uuid'] = svc.conf_get(s, 'uuid')
    except ex.OptNotFound as exc:
        kwargs['uuid'] = exc.default

    m = __import__('resDiskMdLinux')
    r = m.Disk(**kwargs)
    svc += r

def add_drbd(svc, s):
    """Parse the configuration file and add a drbd object for each [drbd#n]
    section. Drbd objects are stored in a list in the service object.
    """
    kwargs = init_kwargs(svc, s)
    kwargs['res'] = svc.conf_get(s, 'res')
    mod = __import__('resDiskDrbd')
    r = mod.Drbd(**kwargs)
    svc += r

def add_vdisk(svc, s):
    kwargs = init_kwargs(svc, s)
    devpath = {}

    for attr, val in svc.config.items(s):
        if 'path@' in attr:
            devpath[attr.replace('path@', '')] = val

    if len(devpath) == 0:
        svc.log.error("path@node must be set in section %s"%s)
        return

    kwargs['devpath'] = devpath
    m = __import__('resDiskVdisk')
    r = m.Disk(**kwargs)
    svc += r

def add_loop(svc, s):
    """Parse the configuration file and add a loop object for each [loop#n]
    section. Loop objects are stored in a list in the service object.
    """
    kwargs = init_kwargs(svc, s)
    kwargs['loopFile'] = svc.conf_get(s, 'file')

    try:
        m = __import__('resDiskLoop'+rcEnv.sysname)
    except ImportError:
        svc.log.error("resDiskLoop%s is not implemented"%rcEnv.sysname)
        return

    r = m.Disk(**kwargs)
    svc += r


def add_disk_disk(svc, s):
    kwargs = init_kwargs(svc, s)

    try:
        kwargs['disk_id'] = svc.conf_get(s, 'disk_id')
    except ex.OptNotFound:
        pass

    m = __import__('resDiskDisk'+rcEnv.sysname)
    r = m.Disk(**kwargs)
    svc += r

def add_disk_gce(svc, s):
    kwargs = init_kwargs(svc, s)
    kwargs['names'] = svc.conf_get(s, 'names')
    kwargs['gce_zone'] = svc.conf_get(s, 'gce_zone')
    m = __import__('resDiskGce')
    r = m.Disk(**kwargs)
    svc += r

def add_disk_amazon(svc, s):
    kwargs = init_kwargs(svc, s)
    kwargs['volumes'] = svc.conf_get(s, 'volumes')
    m = __import__('resDiskAmazon')
    r = m.Disk(**kwargs)
    svc += r

def add_disk_rados(svc, s):
    kwargs = init_kwargs(svc, s)
    kwargs['images'] = svc.conf_get(s, 'images')
    try:
        kwargs['keyring'] = svc.conf_get(s, 'keyring')
    except ex.OptNotFound as exc:
        kwargs['keyring'] = exc.default
    try:
        kwargs['client_id'] = svc.conf_get(s, 'client_id')
    except ex.OptNotFound as exc:
        kwargs['client_id'] = exc.default
    try:
        lock_shared_tag = svc.conf_get(s, 'lock_shared_tag')
    except ex.OptNotFound as exc:
        lock_shared_tag = exc.default
    try:
        lock = svc.conf_get(s, 'lock')
    except ex.OptNotFound as exc:
        lock = exc.default

    try:
        m = __import__('resDiskRados'+rcEnv.sysname)
    except ImportError:
        svc.log.error("disk type rados is not implemented")
        return

    r = m.Disk(**kwargs)
    svc += r

    if not lock:
        return

    # rados locking resource
    kwargs["rid"] = kwargs["rid"]+"lock"
    kwargs["lock"] = lock
    kwargs["lock_shared_tag"] = lock_shared_tag
    r = m.DiskLock(**kwargs)
    svc += r


def add_raw(svc, s):
    kwargs = init_kwargs(svc, s)
    disk_type = "Raw"+rcEnv.sysname
    try:
        zone = svc.conf_get(s, 'zone')
    except ex.OptNotFound as exc:
        zone = exc.default

    kwargs['devs'] = svc.conf_get(s, 'devs')

    if zone is not None:
        kwargs['devs'] = set([dev.replace(":", ":<%s>" % zone) for dev in kwargs['devs']])

    try:
        kwargs['user'] = svc.conf_get(s, 'user')
    except ex.OptNotFound as exc:
        kwargs['user'] = exc.default
    try:
        kwargs['group'] = svc.conf_get(s, 'group')
    except ex.OptNotFound as exc:
        kwargs['group'] = exc.default
    try:
        kwargs['perm'] = svc.conf_get(s, 'perm')
    except ex.OptNotFound as exc:
        kwargs['perm'] = exc.default
    try:
        kwargs['create_char_devices'] = svc.conf_get(s, 'create_char_devices')
    except ex.OptNotFound as exc:
        kwargs['create_char_devices'] = exc.default

    try:
        m = __import__('resDisk'+disk_type)
    except ImportError:
        svc.log.error("disk type %s driver is not implemented"%disk_type)
        return

    r = m.Disk(**kwargs)
    if zone is not None:
        r.tags.add('zone')
        r.tags.add(zone)
    svc += r

def add_gandi(svc, s):
    disk_type = "Gandi"
    kwargs = init_kwargs(svc, s)
    kwargs['cloud_id'] = svc.conf_get(s, 'cloud_id')
    kwargs['name'] = svc.conf_get(s, 'name')

    try:
        kwargs['node'] = svc.conf_get(s, 'node')
    except ex.OptNotFound as exc:
        pass
    try:
        kwargs['user'] = svc.conf_get(s, 'user')
    except ex.OptNotFound as exc:
        pass
    try:
        kwargs['group'] = svc.conf_get(s, 'user')
    except ex.OptNotFound as exc:
        pass
    try:
        kwargs['perm'] = svc.conf_get(s, 'perm')
    except ex.OptNotFound as exc:
        pass

    try:
        m = __import__('resDisk'+disk_type)
    except ImportError:
        svc.log.error("disk type %s is not implemented"%disk_type)
        return

    r = m.Disk(**kwargs)
    svc += r

def add_disk_compat(svc, s):
    try:
        disk_type = svc.conf_get(s, 'type')
    except ex.OptNotFound as exc:
        disk_type = s.split("#")[0]
    if len(disk_type) >= 2:
        disk_type = disk_type[0].upper() + disk_type[1:].lower()

    if disk_type == 'Drbd':
        add_drbd(svc, s)
        return
    if disk_type == 'Vdisk':
        add_vdisk(svc, s)
        return
    if disk_type == 'Vmdg':
        add_vmdg(svc, s)
        return
    if disk_type == 'Pool':
        add_zpool(svc, s)
        return
    if disk_type == 'Zpool':
        add_zpool(svc, s)
        return
    if disk_type == 'Loop':
        add_loop(svc, s)
        return
    if disk_type == 'Md':
        add_md(svc, s)
        return
    if disk_type == 'Lv':
        add_lv(svc, s)
        return
    if disk_type == 'Docker':
        add_docker_vol(svc, s)
        return
    if disk_type == 'Gce':
        add_disk_gce(svc, s)
        return
    if disk_type == 'Disk':
        add_disk_disk(svc, s)
        return
    if disk_type == 'Amazon':
        add_disk_amazon(svc, s)
        return
    if disk_type == 'Rados':
        add_disk_rados(svc, s)
        return
    if disk_type == 'Raw':
        add_raw(svc, s)
        return
    if disk_type == 'Gandi':
        add_gandi(svc, s)
        return
    if disk_type == 'Veritas':
        add_veritas(svc, s)
        return

    raise ex.OptNotFound

def add_veritas(svc, s):
    kwargs = init_kwargs(svc, s)
    kwargs['name'] = svc.conf_get(s, 'name')

    try:
        m = __import__('resDiskVgVeritas')
    except ImportError:
        svc.log.error("disk type veritas is not implemented")
        return

    r = m.Disk(**kwargs)
    svc += r

def add_vg(svc, s):
    try:
        add_disk_compat(svc, s)
        return
    except ex.OptNotFound as exc:
        pass

    disk_type = rcEnv.sysname
    kwargs = init_kwargs(svc, s)
    kwargs['name'] = svc.conf_get(s, 'name')

    try:
        kwargs['dsf'] = svc.conf_get(s, 'dsf')
    except ex.OptNotFound as exc:
        pass

    try:
        m = __import__('resDiskVg'+disk_type)
    except ImportError:
        svc.log.error("disk type %s is not implemented"%disk_type)
        return

    r = m.Disk(**kwargs)
    svc += r

def add_sync(svc, s):
    try:
        rtype = svc.conf_get(s, 'type')
    except ex.OptNotFound as exc:
        rtype = exc.default
    globals()["add_sync_"+rtype](svc, s)

def add_container(svc, s):
    rtype = svc.conf_get(s, 'type')
    globals()["add_container_"+rtype](svc, s)

def add_disk(svc, s):
    """Parse the configuration file and add a disk object for each [disk#n]
    section. Disk objects are stored in a list in the service object.
    """
    try:
        disk_type = svc.conf_get(s, 'type')
    except ex.OptNotFound as exc:
        disk_type = s.split("#")[0]

    if len(disk_type) >= 2:
        disk_type = disk_type[0].upper() + disk_type[1:].lower()

    if disk_type == 'Drbd':
        add_drbd(svc, s)
        return
    if disk_type == 'Vdisk':
        add_vdisk(svc, s)
        return
    if disk_type == 'Vmdg':
        add_vmdg(svc, s)
        return
    if disk_type == 'Pool':
        add_zpool(svc, s)
        return
    if disk_type == 'Zpool':
        add_zpool(svc, s)
        return
    if disk_type == 'Loop':
        add_loop(svc, s)
        return
    if disk_type == 'Lv':
        add_lv(svc, s)
        return
    if disk_type == 'Docker':
        add_docker_vol(svc, s)
        return
    if disk_type == 'Md':
        add_md(svc, s)
        return
    if disk_type == 'Gce':
        add_disk_gce(svc, s)
        return
    if disk_type == 'Disk':
        add_disk_disk(svc, s)
        return
    if disk_type == 'Amazon':
        add_disk_amazon(svc, s)
        return
    if disk_type == 'Rados':
        add_disk_rados(svc, s)
        return
    if disk_type == 'Raw':
        add_raw(svc, s)
        return
    if disk_type == 'Gandi':
        add_gandi(svc, s)
        return
    if disk_type == 'Veritas':
        add_veritas(svc, s)
        return
    if disk_type == 'Lvm' or disk_type == 'Vg' or disk_type == rcEnv.sysname:
        add_vg(svc, s)
        return

def add_vmdg(svc, s):
    kwargs = init_kwargs(svc, s)
    kwargs['container_id'] = svc.conf_get(s, 'container_id')

    if not svc.config.has_section(kwargs['container_id']):
        svc.log.error("%s.container_id points to an invalid section"%kwargs['container_id'])
        return

    try:
        container_type = svc.conf_get(kwargs['container_id'], 'type')
    except ex.OptNotFound as exc:
        svc.log.error("type must be set in section %s"%kwargs['container_id'])
        return

    if container_type == 'ldom':
        m = __import__('resDiskLdom')
    else:
        return

    r = m.Disk(**kwargs)
    svc += r

def add_zpool(svc, s):
    """Parse the configuration file and add a zpool object for each disk.zpool
    section. Pools objects are stored in a list in the service object.
    """
    kwargs = init_kwargs(svc, s)
    kwargs['name'] = svc.conf_get(s, 'name')

    try:
        zone = svc.conf_get(s, 'zone')
    except ex.OptNotFound as exc:
        zone = None

    m = __import__('resDiskZfs')
    r = m.Disk(**kwargs)

    if zone is not None:
        r.tags.add('zone')
        r.tags.add(zone)

    svc += r

def add_share(svc, s):
    _type = svc.conf_get(s, 'type')

    fname = 'add_share_'+_type
    if fname not in globals():
        svc.log.error("type '%s' not supported in section %s"%(_type, s))
    globals()[fname](svc, s)

def add_share_nfs(svc, s):
    kwargs = init_kwargs(svc, s)
    kwargs['path'] = svc.conf_get(s, 'path')
    kwargs['opts'] = svc.conf_get(s, 'opts')

    try:
        m = __import__('resShareNfs'+rcEnv.sysname)
    except ImportError:
        svc.log.error("resShareNfs%s is not implemented"%rcEnv.sysname)
        return

    r = m.Share(**kwargs)
    svc += r

def add_fs_docker(svc, s):
    kwargs = init_kwargs(svc, s)
    try:
        kwargs['driver'] = svc.conf_get(s, 'driver')
    except ex.OptNotFound as exc:
        kwargs['driver'] = exc.default
    try:
        kwargs['options'] = svc.conf_get(s, 'options')
    except ex.OptNotFound as exc:
        kwargs['options'] = exc.default
    m = __import__('resFsDocker')
    r = m.Fs(**kwargs)
    svc += r

def add_fs_directory(svc, s):
    kwargs = init_kwargs(svc, s)
    kwargs['path'] = svc.conf_get(s, 'path')

    try:
        kwargs['user'] = svc.conf_get(s, 'user')
    except ex.OptNotFound as exc:
        pass

    try:
        kwargs['group'] = svc.conf_get(s, 'group')
    except ex.OptNotFound as exc:
        pass

    try:
        kwargs['perm'] = svc.conf_get(s, 'perm')
    except ex.OptNotFound as exc:
        pass

    try:
        zone = svc.conf_get(s, 'zone')
    except:
        zone = None

    if zone is not None:
        zp = None
        for r in svc.get_resources("container.zone", discard_disabled=False):
            if r.name == zone:
                try:
                    zp = r.get_zonepath()
                except:
                    zp = "<%s>" % zone
                break
        if zp is None:
            svc.log.error("zone %s, referenced in %s, not found"%(zone, s))
            raise ex.excError()
        kwargs['path'] = zp+'/root'+kwargs['path']
        if "<%s>" % zone != zp:
            kwargs['path'] = os.path.realpath(kwargs['path'])

    mod = __import__('resFsDir')
    r = mod.FsDir(**kwargs)

    if zone is not None:
        r.tags.add(zone)
        r.tags.add('zone')

    svc += r

def add_fs(svc, s):
    """Parse the configuration file and add a fs object for each [fs#n]
    section. Fs objects are stored in a list in the service object.
    """
    kwargs = init_kwargs(svc, s)

    try:
        kwargs['fs_type'] = svc.conf_get(s, 'type')
    except ex.OptNotFound as exc:
        kwargs['fs_type'] = ""

    if kwargs['fs_type'] == "directory":
        add_fs_directory(svc, s)
        return

    if kwargs['fs_type'] == "docker":
        add_fs_docker(svc, s)
        return

    kwargs['device'] = svc.conf_get(s, 'dev')
    kwargs['mount_point'] = svc.conf_get(s, 'mnt')

    if kwargs['mount_point'] and kwargs['mount_point'][-1] != "/" and kwargs['mount_point'][-1] == '/':
        # Remove trailing / to not risk losing rsync src trailing / upon snap
        # mountpoint substitution.
        kwargs['mount_point'] = kwargs['mount_point'][0:-1]

    try:
        kwargs['mount_options'] = svc.conf_get(s, 'mnt_opt')
    except ex.OptNotFound as exc:
        kwargs['mount_options'] = ""

    try:
        kwargs['snap_size'] = svc.conf_get(s, 'snap_size')
    except ex.OptNotFound as exc:
        pass

    try:
        zone = svc.conf_get(s, 'zone')
    except:
        zone = None

    if zone is not None:
        zp = None
        for r in svc.get_resources("container.zone", discard_disabled=False):
            if r.name == zone:
                try:
                    zp = r.get_zonepath()
                except:
                    zp = "<%s>" % zone
                break
        if zp is None:
            svc.log.error("zone %s, referenced in %s, not found"%(zone, s))
            raise ex.excError()
        kwargs['mount_point'] = zp+'/root'+kwargs['mount_point']
        if "<%s>" % zone != zp:
            kwargs['mount_point'] = os.path.realpath(kwargs['mount_point'])

    try:
        mount = __import__('resFs'+rcEnv.sysname)
    except ImportError:
        svc.log.error("resFs%s is not implemented"%rcEnv.sysname)
        return

    r = mount.Mount(**kwargs)

    if zone is not None:
        r.tags.add(zone)
        r.tags.add('zone')

    svc += r

def add_container_esx(svc, s):
    kwargs = init_kwargs(svc, s)
    kwargs['osvc_root_path'] = get_osvc_root_path(svc, s)

    try:
        kwargs['name'] = svc.conf_get(s, 'name')
    except ex.OptNotFound as exc:
        kwargs['name'] = svc.svcname

    try:
        kwargs['guestos'] = svc.conf_get(s, 'guestos')
    except ex.OptNotFound as exc:
        pass

    m = __import__('resContainerEsx')
    r = m.Esx(**kwargs)
    svc += r

def add_container_hpvm(svc, s):
    kwargs = init_kwargs(svc, s)
    kwargs['osvc_root_path'] = get_osvc_root_path(svc, s)

    try:
        kwargs['name'] = svc.conf_get(s, 'name')
    except ex.OptNotFound as exc:
        kwargs['name'] = svc.svcname

    try:
        kwargs['guestos'] = svc.conf_get(s, 'guestos')
    except ex.OptNotFound as exc:
        pass

    m = __import__('resContainerHpVm')
    r = m.HpVm(**kwargs)
    svc += r

def add_container_ldom(svc, s):
    kwargs = init_kwargs(svc, s)
    kwargs['osvc_root_path'] = get_osvc_root_path(svc, s)

    try:
        kwargs['name'] = svc.conf_get(s, 'name')
    except ex.OptNotFound as exc:
        kwargs['name'] = svc.svcname

    try:
        kwargs['guestos'] = svc.conf_get(s, 'guestos')
    except ex.OptNotFound as exc:
        pass

    m = __import__('resContainerLdom')
    r = m.Ldom(**kwargs)
    svc += r

def add_container_vbox(svc, s):
    kwargs = init_kwargs(svc, s)
    kwargs['osvc_root_path'] = get_osvc_root_path(svc, s)

    try:
        kwargs['name'] = svc.conf_get(s, 'name')
    except ex.OptNotFound as exc:
        kwargs['name'] = svc.svcname

    try:
        kwargs['guestos'] = svc.conf_get(s, 'guestos')
    except ex.OptNotFound as exc:
        pass

    m = __import__('resContainerVbox')
    r = m.Vbox(**kwargs)
    svc += r

def add_container_xen(svc, s):
    kwargs = init_kwargs(svc, s)
    kwargs['osvc_root_path'] = get_osvc_root_path(svc, s)

    try:
        kwargs['name'] = svc.conf_get(s, 'name')
    except ex.OptNotFound as exc:
        kwargs['name'] = svc.svcname

    try:
        kwargs['guestos'] = svc.conf_get(s, 'guestos')
    except ex.OptNotFound as exc:
        pass

    m = __import__('resContainerXen')
    r = m.Xen(**kwargs)
    svc += r

def add_container_zone(svc, s):
    kwargs = init_kwargs(svc, s)
    kwargs['osvc_root_path'] = get_osvc_root_path(svc, s)

    try:
        kwargs['name'] = svc.conf_get(s, 'name')
    except ex.OptNotFound as exc:
        kwargs['name'] = svc.svcname

    try:
        kwargs['guestos'] = svc.conf_get(s, 'guestos')
    except ex.OptNotFound as exc:
        pass

    try:
        kwargs['delete_on_stop'] = svc.conf_get(s, 'delete_on_stop')
    except ex.OptNotFound as exc:
        pass

    m = __import__('resContainerZone')
    r = m.Zone(**kwargs)
    svc += r

def add_container_vcloud(svc, s):
    kwargs = init_kwargs(svc, s)
    kwargs['osvc_root_path'] = get_osvc_root_path(svc, s)

    try:
        kwargs['name'] = svc.conf_get(s, 'name')
    except ex.OptNotFound as exc:
        kwargs['name'] = svc.svcname

    try:
        kwargs['guestos'] = svc.conf_get(s, 'guestos')
    except ex.OptNotFound as exc:
        pass

    kwargs['cloud_id'] = svc.conf_get(s, 'cloud_id')
    kwargs['vapp'] = svc.conf_get(s, 'vapp')
    kwargs['key_name'] = svc.conf_get(s, 'key_name')

    m = __import__('resContainerVcloud')
    r = m.CloudVm(**kwargs)
    svc += r

def add_container_amazon(svc, s):
    kwargs = init_kwargs(svc, s)
    kwargs['osvc_root_path'] = get_osvc_root_path(svc, s)

    # mandatory keywords
    try:
        kwargs['name'] = svc.conf_get(s, 'name')
    except ex.OptNotFound as exc:
        kwargs['name'] = svc.svcname

    kwargs['cloud_id'] = svc.conf_get(s, 'cloud_id')
    kwargs['key_name'] = svc.conf_get(s, 'key_name')

    # optional keywords
    try:
        kwargs['guestos'] = svc.conf_get(s, 'guestos')
    except ex.OptNotFound as exc:
        pass

    # provisioning keywords
    try:
        kwargs['image_id'] = svc.conf_get(s, 'image_id')
    except ex.OptNotFound as exc:
        pass

    try:
        kwargs['size'] = svc.conf_get(s, 'size')
    except ex.OptNotFound as exc:
        pass


    try:
        kwargs['subnet'] = svc.conf_get(s, 'subnet')
    except ex.OptNotFound as exc:
        pass

    m = __import__('resContainerAmazon')
    r = m.CloudVm(**kwargs)
    svc += r

def add_container_openstack(svc, s):
    kwargs = init_kwargs(svc, s)
    kwargs['osvc_root_path'] = get_osvc_root_path(svc, s)

    try:
        kwargs['name'] = svc.conf_get(s, 'name')
    except ex.OptNotFound as exc:
        kwargs['name'] = svc.svcname

    try:
        kwargs['guestos'] = svc.conf_get(s, 'guestos')
    except ex.OptNotFound as exc:
        pass

    kwargs['cloud_id'] = svc.conf_get(s, 'cloud_id')
    kwargs['key_name'] = svc.conf_get(s, 'key_name')

    try:
        kwargs['size'] = svc.conf_get(s, 'size')
    except ex.OptNotFound as exc:
        pass

    try:
        kwargs['shared_ip_group'] = svc.conf_get(s, 'shared_ip_group')
    except ex.OptNotFound as exc:
        pass

    m = __import__('resContainerOpenstack')
    r = m.CloudVm(**kwargs)
    svc += r

def add_container_vz(svc, s):
    kwargs = init_kwargs(svc, s)
    kwargs['osvc_root_path'] = get_osvc_root_path(svc, s)

    try:
        kwargs['name'] = svc.conf_get(s, 'name')
    except ex.OptNotFound as exc:
        kwargs['name'] = svc.svcname

    try:
        kwargs['guestos'] = svc.conf_get(s, 'guestos')
    except ex.OptNotFound as exc:
        pass

    m = __import__('resContainerVz')
    r = m.Vz(**kwargs)
    svc += r

def add_container_kvm(svc, s):
    kwargs = init_kwargs(svc, s)
    kwargs['osvc_root_path'] = get_osvc_root_path(svc, s)

    try:
        kwargs['name'] = svc.conf_get(s, 'name')
    except ex.OptNotFound as exc:
        kwargs['name'] = svc.svcname

    try:
        kwargs['guestos'] = svc.conf_get(s, 'guestos')
    except ex.OptNotFound as exc:
        pass

    m = __import__('resContainerKvm')
    r = m.Kvm(**kwargs)
    svc += r

def add_container_srp(svc, s):
    kwargs = init_kwargs(svc, s)
    kwargs['osvc_root_path'] = get_osvc_root_path(svc, s)

    try:
        kwargs['name'] = svc.conf_get(s, 'name')
    except ex.OptNotFound as exc:
        kwargs['name'] = svc.svcname

    try:
        kwargs['guestos'] = svc.conf_get(s, 'guestos')
    except ex.OptNotFound as exc:
        pass

    m = __import__('resContainerSrp')
    r = m.Srp(**kwargs)
    svc += r

def add_container_lxd(svc, s):
    kwargs = init_kwargs(svc, s)
    kwargs['osvc_root_path'] = get_osvc_root_path(svc, s)

    try:
        kwargs['name'] = svc.conf_get(s, 'name')
    except ex.OptNotFound as exc:
        kwargs['name'] = svc.svcname

    m = __import__('resContainerLxd')
    r = m.Container(**kwargs)
    svc += r

def add_container_lxc(svc, s):
    kwargs = init_kwargs(svc, s)
    kwargs['osvc_root_path'] = get_osvc_root_path(svc, s)
    kwargs['rcmd'] = get_rcmd(svc, s)

    try:
        kwargs['name'] = svc.conf_get(s, 'name')
    except ex.OptNotFound as exc:
        kwargs['name'] = svc.svcname

    try:
        kwargs['guestos'] = svc.conf_get(s, 'guestos')
    except ex.OptNotFound as exc:
        pass

    try:
        kwargs['cf'] = svc.conf_get(s, 'cf')
    except ex.OptNotFound as exc:
        pass

    m = __import__('resContainerLxc')
    r = m.Lxc(**kwargs)
    svc += r

def add_container_docker(svc, s):
    kwargs = init_kwargs(svc, s)
    kwargs['osvc_root_path'] = get_osvc_root_path(svc, s)
    kwargs['run_image'] = svc.conf_get(s, 'run_image')

    try:
        kwargs['run_command'] = svc.conf_get(s, 'run_command')
    except ex.OptNotFound as exc:
        pass

    try:
        kwargs['run_args'] = svc.conf_get(s, 'run_args')
    except ex.OptNotFound as exc:
        pass

    try:
        kwargs['docker_service'] = svc.conf_get(s, 'docker_service')
    except ex.OptNotFound as exc:
        kwargs['docker_service'] = exc.default

    m = __import__('resContainerDocker')
    r = m.Docker(**kwargs)
    svc += r

def add_container_ovm(svc, s):
    kwargs = init_kwargs(svc, s)
    kwargs['osvc_root_path'] = get_osvc_root_path(svc, s)
    try:
        kwargs['uuid'] = svc.conf_get(s, 'uuid')
    except ex.OptNotFound as exc:
        kwargs['uuid'] = exc.default

    try:
        kwargs['name'] = svc.conf_get(s, 'name')
    except ex.OptNotFound as exc:
        kwargs['name'] = svc.svcname

    try:
        kwargs['guestos'] = svc.conf_get(s, 'guestos')
    except ex.OptNotFound as exc:
        pass

    m = __import__('resContainerOvm')
    r = m.Ovm(**kwargs)
    svc += r

def add_container_jail(svc, s):
    kwargs = init_kwargs(svc, s)
    kwargs['osvc_root_path'] = get_osvc_root_path(svc, s)
    kwargs['jailroot'] = svc.conf_get(s, 'jailroot')

    try:
        kwargs['name'] = svc.conf_get(s, 'name')
    except ex.OptNotFound as exc:
        kwargs['name'] = svc.svcname

    try:
        kwargs['ips'] = svc.conf_get(s, 'ips')
    except ex.OptNotFound as exc:
        kwargs['ips'] = exc.default

    try:
        kwargs['ip6s'] = svc.conf_get(s, 'ip6s')
    except ex.OptNotFound as exc:
        kwargs['ip6s'] = exc.default

    m = __import__('resContainerJail')
    r = m.Jail(**kwargs)
    svc += r

def add_mandatory_syncs(svc):
    """
    Mandatory files to sync.
    To all nodes, service definition and files contributed by resources.
    """

    def add_file(flist, fpath):
        if not os.path.exists(fpath):
            return flist
        flist.append(fpath)
        return flist

    target = set(["nodes", "drpnodes"])
    if svc.scale_target is not None or len(svc.nodes) < 2 or \
       len(svc.resources_by_id) == 0:
        target.remove("nodes")
    if rcEnv.nodename in svc.nodes and len(svc.drpnodes) == 0:
        target.remove("drpnodes")
    if rcEnv.nodename in svc.drpnodes and len(svc.drpnodes) < 2:
        target.remove("drpnodes")
    if len(target) == 0:
        return

    kwargs = {}
    src = []
    src = add_file(src, svc.paths.exe)
    src = add_file(src, svc.paths.cf)
    src = add_file(src, svc.paths.initd)
    src = add_file(src, svc.paths.alt_initd)
    dst = os.path.join("/")
    exclude = ['--exclude=*.core']
    kwargs['rid'] = "sync#i0"
    kwargs['src'] = src
    kwargs['dst'] = dst
    kwargs['options'] = ['-R']+exclude
    if svc.config.has_option(kwargs['rid'], 'options'):
        kwargs['options'] += cmdline2list(svc.config.get(kwargs['rid'], 'options'))
    kwargs['target'] = list(target)
    kwargs['internal'] = True
    kwargs['disabled'] = get_disabled(svc, kwargs['rid'])
    kwargs['optional'] = get_optional(svc, kwargs['rid'])
    kwargs.update(get_sync_args(svc, kwargs['rid']))
    r = resSyncRsync.Rsync(**kwargs)
    svc += r

def add_sync_docker(svc, s):
    kwargs = {}

    kwargs['target'] = svc.conf_get(s, 'target')

    kwargs['rid'] = s
    kwargs['subset'] = get_subset(svc, s)
    kwargs['tags'] = get_tags(svc, s)
    kwargs['disabled'] = get_disabled(svc, s)
    kwargs['optional'] = get_optional(svc, s)
    kwargs.update(get_sync_args(svc, s))

    m = __import__('resSyncDocker')
    r = m.SyncDocker(**kwargs)
    svc += r

def add_sync_btrfs(svc, s):
    kwargs = {}

    kwargs['src'] = svc.conf_get(s, 'src')
    kwargs['dst'] = svc.conf_get(s, 'dst')
    kwargs['target'] = svc.conf_get(s, 'target')

    try:
        kwargs['recursive'] = svc.conf_get(s, 'recursive')
    except ex.OptNotFound as exc:
        kwargs['recursive'] = exc.default

    kwargs['rid'] = s
    kwargs['subset'] = get_subset(svc, s)
    kwargs['tags'] = get_tags(svc, s)
    kwargs['disabled'] = get_disabled(svc, s)
    kwargs['optional'] = get_optional(svc, s)
    kwargs.update(get_sync_args(svc, s))
    btrfs = __import__('resSyncBtrfs')
    r = btrfs.SyncBtrfs(**kwargs)
    svc += r

def add_sync_zfs(svc, s):
    kwargs = {}

    kwargs['src'] = svc.conf_get(s, 'src')
    kwargs['dst'] = svc.conf_get(s, 'dst')
    kwargs['target'] = svc.conf_get(s, 'target')

    try:
        kwargs['recursive'] = svc.conf_get(s, 'recursive')
    except ex.OptNotFound as exc:
        kwargs['recursive'] = exc.default

    kwargs['rid'] = s
    kwargs['subset'] = get_subset(svc, s)
    kwargs['tags'] = get_tags(svc, s)
    kwargs['disabled'] = get_disabled(svc, s)
    kwargs['optional'] = get_optional(svc, s)
    kwargs.update(get_sync_args(svc, s))
    zfs = __import__('resSyncZfs')
    r = zfs.SyncZfs(**kwargs)
    svc += r

def add_sync_dds(svc, s):
    kwargs = {}

    kwargs['src'] = svc.conf_get(s, 'src')
    kwargs['target'] = svc.conf_get(s, 'target')

    dsts = {}
    for node in svc.nodes | svc.drpnodes:
        dst = svc.conf_get(s, 'dst', impersonate=node)
        dsts[node] = dst

    if len(dsts) == 0:
        for node in svc.nodes | svc.drpnodes:
            dsts[node] = kwargs['src']

    kwargs['dsts'] = dsts

    try:
        kwargs['snap_size'] = svc.conf_get(s, 'snap_size')
    except ex.OptNotFound as exc:
        pass

    kwargs['rid'] = s
    kwargs['subset'] = get_subset(svc, s)
    kwargs['tags'] = get_tags(svc, s)
    kwargs['disabled'] = get_disabled(svc, s)
    kwargs['optional'] = get_optional(svc, s)
    kwargs.update(get_sync_args(svc, s))
    dds = __import__('resSyncDds')
    r = dds.syncDds(**kwargs)
    svc += r

def add_sync_dcsckpt(svc, s):
    kwargs = {}

    kwargs['dcs'] = svc.conf_get(s, 'dcs')
    kwargs['manager'] = svc.conf_get(s, 'manager')
    raw_pairs = svc.conf_get(s, 'pairs')

    import json
    try:
        pairs = json.loads(raw_pairs)
        if len(pairs) == 0:
            svc.log.error("config file section %s must have 'pairs' set" % s)
            return
    except:
        svc.log.error("json error parsing 'pairs' in section %s" % s)
    kwargs['pairs'] = pairs

    kwargs['rid'] = s
    kwargs['subset'] = get_subset(svc, s)
    kwargs['tags'] = get_tags(svc, s)
    kwargs['disabled'] = get_disabled(svc, s)
    kwargs['optional'] = get_optional(svc, s)
    kwargs.update(get_sync_args(svc, s))
    try:
        sc = __import__('resSyncDcsCkpt'+rcEnv.sysname)
    except:
        sc = __import__('resSyncDcsCkpt')
    r = sc.syncDcsCkpt(**kwargs)
    svc += r

def add_sync_dcssnap(svc, s):
    kwargs = {}

    try:
        kwargs['dcs'] = svc.conf_get(s, 'dcs')
    except ex.OptNotFound as exc:
        svc.log.error("config file section %s must have 'dcs' set" % s)
        return

    try:
        kwargs['manager'] = svc.conf_get(s, 'manager')
    except ex.OptNotFound as exc:
        svc.log.error("config file section %s must have 'manager' set" % s)
        return

    try:
        kwargs['snapname'] = svc.conf_get(s, 'snapname')
    except ex.OptNotFound as exc:
        svc.log.error("config file section %s must have 'snapname' set" % s)
        return

    kwargs['rid'] = s
    kwargs['subset'] = get_subset(svc, s)
    kwargs['tags'] = get_tags(svc, s)
    kwargs['disabled'] = get_disabled(svc, s)
    kwargs['optional'] = get_optional(svc, s)
    kwargs.update(get_sync_args(svc, s))
    try:
        sc = __import__('resSyncDcsSnap'+rcEnv.sysname)
    except:
        sc = __import__('resSyncDcsSnap')
    r = sc.syncDcsSnap(**kwargs)
    svc += r

def add_sync_s3(svc, s):
    kwargs = {}

    try:
        kwargs['full_schedule'] = svc.conf_get(s, 'full_schedule')
    except ex.OptNotFound as exc:
        kwargs['full_schedule'] = exc.default

    try:
        kwargs['options'] = svc.conf_get(s, 'options')
    except ex.OptNotFound as exc:
        kwargs['options'] = exc.default

    try:
        kwargs['snar'] = svc.conf_get(s, 'snar')
    except ex.OptNotFound as exc:
        kwargs['snar'] = exc.default

    kwargs['bucket'] = svc.conf_get(s, 'bucket')
    kwargs['src'] = svc.conf_get(s, 'src')

    kwargs['rid'] = s
    kwargs['subset'] = get_subset(svc, s)
    kwargs['tags'] = get_tags(svc, s)
    kwargs['disabled'] = get_disabled(svc, s)
    kwargs['optional'] = get_optional(svc, s)
    kwargs.update(get_sync_args(svc, s))
    sc = __import__('resSyncS3')
    r = sc.syncS3(**kwargs)
    svc += r

def add_sync_zfssnap(svc, s):
    kwargs = {}

    try:
        kwargs['name'] = svc.conf_get(s, 'name')
    except ex.OptNotFound as exc:
        pass

    try:
        kwargs['keep'] = svc.conf_get(s, 'keep')
    except ex.OptNotFound as exc:
        pass

    try:
        kwargs['recursive'] = svc.conf_get(s, 'recursive')
    except ex.OptNotFound as exc:
        pass

    kwargs['dataset'] = svc.conf_get(s, 'dataset')

    kwargs['rid'] = s
    kwargs['subset'] = get_subset(svc, s)
    kwargs['tags'] = get_tags(svc, s)
    kwargs['disabled'] = get_disabled(svc, s)
    kwargs['optional'] = get_optional(svc, s)
    kwargs.update(get_sync_args(svc, s))
    sc = __import__('resSyncZfsSnap')
    r = sc.syncZfsSnap(**kwargs)
    svc += r

def add_sync_btrfssnap(svc, s):
    kwargs = {}

    try:
        kwargs['name'] = svc.conf_get(s, 'name')
    except ex.OptNotFound as exc:
        pass

    try:
        kwargs['keep'] = svc.conf_get(s, 'keep')
    except ex.OptNotFound as exc:
        pass

    kwargs['subvol'] = svc.conf_get(s, 'subvol')

    kwargs['rid'] = s
    kwargs['subset'] = get_subset(svc, s)
    kwargs['tags'] = get_tags(svc, s)
    kwargs['disabled'] = get_disabled(svc, s)
    kwargs['optional'] = get_optional(svc, s)
    kwargs.update(get_sync_args(svc, s))
    sc = __import__('resSyncBtrfsSnap')
    r = sc.syncBtrfsSnap(**kwargs)
    svc += r

def add_sync_necismsnap(svc, s):
    kwargs = {}

    try:
        kwargs['array'] = svc.conf_get(s, 'array')
    except ex.OptNotFound as exc:
        pass

    try:
        kwargs['devs'] = svc.conf_get(s, 'devs')
    except ex.OptNotFound as exc:
        svc.log.error("config file section %s must have devs set" % s)
        return

    kwargs['rid'] = s
    kwargs['subset'] = get_subset(svc, s)
    kwargs['tags'] = get_tags(svc, s)
    kwargs['disabled'] = get_disabled(svc, s)
    kwargs['optional'] = get_optional(svc, s)
    kwargs.update(get_sync_args(svc, s))
    try:
        sc = __import__('resSyncNecIsmSnap'+rcEnv.sysname)
    except:
        sc = __import__('resSyncNecIsmSnap')
    r = sc.syncNecIsmSnap(**kwargs)
    svc += r

def add_sync_evasnap(svc, s):
    kwargs = {}

    try:
        kwargs['eva_name'] = svc.conf_get(s, 'eva_name')
    except ex.OptNotFound as exc:
        svc.log.error("config file section %s must have eva_name set" % s)
        return

    try:
        kwargs['snap_name'] = svc.conf_get(s, 'snap_name')
    except ex.OptNotFound as exc:
        kwargs['snap_name'] = svc.svcname

    import json
    pairs = []
    if 'pairs' in svc.config.options(s):
        pairs = json.loads(svc.config.get(s, 'pairs'))
    if len(pairs) == 0:
        svc.log.error("config file section %s must have pairs set" % s)
        return
    else:
        kwargs['pairs'] = pairs

    kwargs['rid'] = s
    kwargs['subset'] = get_subset(svc, s)
    kwargs['tags'] = get_tags(svc, s)
    kwargs['disabled'] = get_disabled(svc, s)
    kwargs['optional'] = get_optional(svc, s)
    kwargs.update(get_sync_args(svc, s))
    try:
        sc = __import__('resSyncEvasnap'+rcEnv.sysname)
    except:
        sc = __import__('resSyncEvasnap')
    r = sc.syncEvasnap(**kwargs)
    svc += r

def add_sync_hp3parsnap(svc, s):
    kwargs = {}

    kwargs['array'] = svc.conf_get(s, 'array')
    vv_names = svc.conf_get(s, 'vv_names')

    if len(vv_names) == 0:
        svc.log.error("config file section %s must have at least one vv_name set" % s)
        return

    kwargs['vv_names'] = vv_names

    kwargs['rid'] = s
    kwargs['subset'] = get_subset(svc, s)
    kwargs['tags'] = get_tags(svc, s)
    kwargs['disabled'] = get_disabled(svc, s)
    kwargs['optional'] = get_optional(svc, s)
    kwargs.update(get_sync_args(svc, s))
    try:
        sc = __import__('resSyncHp3parSnap'+rcEnv.sysname)
    except:
        sc = __import__('resSyncHp3parSnap')
    r = sc.syncHp3parSnap(**kwargs)
    svc += r

def add_sync_hp3par(svc, s):
    kwargs = {}

    kwargs['mode'] = svc.conf_get(s, 'mode')
    kwargs['array'] = svc.conf_get(s, 'array')

    rcg_names = {}
    for node in svc.nodes | svc.drpnodes:
        array = svc.conf_get(s, 'array', impersonate=node)
        rcg = svc.conf_get(s, 'rcg', impersonate=node)
        rcg_names[array] = rcg

    if len(rcg_names) == 0:
        svc.log.error("config file section %s must have rcg set" % s)
        return

    kwargs['rcg_names'] = rcg_names

    kwargs['rid'] = s
    kwargs['subset'] = get_subset(svc, s)
    kwargs['tags'] = get_tags(svc, s)
    kwargs['disabled'] = get_disabled(svc, s)
    kwargs['optional'] = get_optional(svc, s)
    kwargs.update(get_sync_args(svc, s))
    try:
        sc = __import__('resSyncHp3par'+rcEnv.sysname)
    except:
        sc = __import__('resSyncHp3par')
    r = sc.syncHp3par(**kwargs)
    svc += r

def add_sync_symsrdfs(svc, s):
    kwargs = {}

    kwargs['symdg'] = svc.conf_get(s, 'symdg')
    kwargs['rdfg'] = svc.conf_get(s, 'rdfg')
    kwargs['symid'] = svc.conf_get(s, 'symid')

    kwargs['rid'] = s
    kwargs['subset'] = get_subset(svc, s)
    kwargs['tags'] = get_tags(svc, s)
    kwargs['disabled'] = get_disabled(svc, s)
    kwargs['optional'] = get_optional(svc, s)
    kwargs.update(get_sync_args(svc, s))
    try:
        sc = __import__('resSyncSymSrdfS'+rcEnv.sysname)
    except:
        sc = __import__('resSyncSymSrdfS')
    r = sc.syncSymSrdfS(**kwargs)
    svc += r


def add_sync_radosclone(svc, s):
    kwargs = {}

    try:
        kwargs['client_id'] = svc.conf_get(s, 'client_id')
    except ex.OptNotFound as exc:
        pass

    try:
        kwargs['keyring'] = svc.conf_get(s, 'keyring')
    except ex.OptNotFound as exc:
        pass

    kwargs['pairs'] = svc.conf_get(s, 'pairs')

    kwargs['rid'] = s
    kwargs['subset'] = get_subset(svc, s)
    kwargs['tags'] = get_tags(svc, s)
    kwargs['disabled'] = get_disabled(svc, s)
    kwargs['optional'] = get_optional(svc, s)
    kwargs.update(get_sync_args(svc, s))
    try:
        sc = __import__('resSyncRados'+rcEnv.sysname)
    except:
        sc = __import__('resSyncRados')
    r = sc.syncRadosClone(**kwargs)
    svc += r

def add_sync_radossnap(svc, s):
    kwargs = {}

    try:
        kwargs['client_id'] = svc.conf_get(s, 'client_id')
    except ex.OptNotFound:
        pass

    try:
        kwargs['keyring'] = svc.conf_get(s, 'keyring')
    except ex.OptNotFound:
        pass

    kwargs['images'] = svc.conf_get(s, 'images')

    kwargs['rid'] = s
    kwargs['subset'] = get_subset(svc, s)
    kwargs['tags'] = get_tags(svc, s)
    kwargs['disabled'] = get_disabled(svc, s)
    kwargs['optional'] = get_optional(svc, s)
    kwargs.update(get_sync_args(svc, s))
    try:
        sc = __import__('resSyncRados'+rcEnv.sysname)
    except:
        sc = __import__('resSyncRados')
    r = sc.syncRadosSnap(**kwargs)
    svc += r

def add_sync_symsnap(svc, s):
    _add_sync_symclone(svc, s, "sync.symsnap")

def add_sync_symclone(svc, s):
    _add_sync_symclone(svc, s, "sync.symclone")

def _add_sync_symclone(svc, s, t):
    kwargs = {}
    kwargs['type'] = t
    kwargs['pairs'] = svc.conf_get(s, 'pairs')
    kwargs['symid'] = svc.conf_get(s, 'symid')

    try:
        kwargs['recreate_timeout'] = svc.conf_get(s, 'recreate_timeout')
    except ex.OptNotFound as exc:
        kwargs['recreate_timeout'] = exc.default

    try:
        kwargs['restore_timeout'] = svc.conf_get(s, 'restore_timeout')
    except ex.OptNotFound as exc:
        kwargs['restore_timeout'] = exc.default

    try:
        kwargs['consistent'] = svc.conf_get(s, 'consistent')
    except ex.OptNotFound as exc:
        kwargs['consistent'] = exc.default

    try:
        kwargs['precopy'] = svc.conf_get(s, 'precopy')
    except ex.OptNotFound as exc:
        kwargs['precopy'] = exc.default

    kwargs['rid'] = s
    kwargs['subset'] = get_subset(svc, s)
    kwargs['tags'] = get_tags(svc, s)
    kwargs['disabled'] = get_disabled(svc, s)
    kwargs['optional'] = get_optional(svc, s)
    kwargs.update(get_sync_args(svc, s))
    try:
        sc = __import__('resSyncSymclone'+rcEnv.sysname)
    except:
        sc = __import__('resSyncSymclone')
    r = sc.syncSymclone(**kwargs)
    svc += r

def add_sync_ibmdssnap(svc, s):
    kwargs = {}

    kwargs['pairs'] = svc.conf_get(s, 'pairs')
    kwargs['array'] = svc.conf_get(s, 'array')
    kwargs['bgcopy'] = svc.conf_get(s, 'bgcopy')
    kwargs['recording'] = svc.conf_get(s, 'recording')

    kwargs['rid'] = s
    kwargs['subset'] = get_subset(svc, s)
    kwargs['tags'] = get_tags(svc, s)
    kwargs['disabled'] = get_disabled(svc, s)
    kwargs['optional'] = get_optional(svc, s)
    kwargs.update(get_sync_args(svc, s))
    try:
        m = __import__('resSyncIbmdsSnap'+rcEnv.sysname)
    except:
        m = __import__('resSyncIbmdsSnap')
    r = m.syncIbmdsSnap(**kwargs)
    svc += r

def add_sync_nexenta(svc, s):
    kwargs = {}

    kwargs['name'] = svc.conf_get(s, 'name')
    kwargs['path'] = svc.conf_get(s, 'path')
    kwargs['reversible'] = svc.conf_get(s, "reversible")

    filers = {}
    for n in svc.nodes | svc.drpnodes:
        filers[n] = svc.conf_get(s, 'filer', impersonate=n)

    kwargs['filers'] = filers
    kwargs['rid'] = s
    kwargs['subset'] = get_subset(svc, s)
    kwargs['tags'] = get_tags(svc, s)
    kwargs['disabled'] = get_disabled(svc, s)
    kwargs['optional'] = get_optional(svc, s)
    kwargs.update(get_sync_args(svc, s))

    import resSyncNexenta
    r = resSyncNexenta.syncNexenta(**kwargs)
    svc += r

def add_sync_netapp(svc, s):
    kwargs = {}

    kwargs['path'] = svc.conf_get(s, 'path')
    kwargs['user'] = svc.conf_get(s, 'user')

    filers = {}
    for n in svc.nodes | svc.drpnodes:
        filers[n] = svc.conf_get(s, 'filer', impersonate=n)

    kwargs['filers'] = filers
    kwargs['rid'] = s
    kwargs['subset'] = get_subset(svc, s)
    kwargs['tags'] = get_tags(svc, s)
    kwargs['disabled'] = get_disabled(svc, s)
    kwargs['optional'] = get_optional(svc, s)
    kwargs.update(get_sync_args(svc, s))

    import resSyncNetapp
    r = resSyncNetapp.syncNetapp(**kwargs)
    svc += r

def add_sync_rsync(svc, s):
    if s.startswith("sync#i"):
        # internal syncs have their own dedicated add function
        return

    kwargs = {}
    kwargs['src'] = []
    _s = svc.conf_get(s, 'src')
    for src in _s:
        kwargs['src'] += glob.glob(src)

    kwargs['dst'] = svc.conf_get(s, 'dst')

    try:
        kwargs['options'] = svc.conf_get(s, 'options')
    except ex.OptNotFound as exc:
        kwargs['options'] = exc.default

    try:
        kwargs['dstfs'] = svc.conf_get(s, 'dstfs')
    except ex.OptNotFound as exc:
        kwargs['dstfs'] = exc.default

    try:
        kwargs['snap'] = svc.conf_get(s, 'snap')
    except ex.OptNotFound as exc:
        kwargs['snap'] = exc.default

    try:
        kwargs['bwlimit'] = svc.conf_get(s, 'bwlimit')
    except ex.OptNotFound as exc:
        pass

    kwargs['target'] = svc.conf_get(s, 'target')
    kwargs['rid'] = s
    kwargs['subset'] = get_subset(svc, s)
    kwargs['tags'] = get_tags(svc, s)
    kwargs['disabled'] = get_disabled(svc, s)
    kwargs['optional'] = get_optional(svc, s)
    kwargs.update(get_sync_args(svc, s))

    r = resSyncRsync.Rsync(**kwargs)
    svc += r

def add_task(svc, s):
    kwargs = init_kwargs(svc, s)

    kwargs['command'] = svc.conf_get(s, 'command')

    try:
        kwargs['on_error'] = svc.conf_get(s, 'on_error')
    except ex.OptNotFound as exc:
        kwargs['on_error'] = exc.default

    try:
        kwargs['user'] = svc.conf_get(s, 'user')
    except ex.OptNotFound as exc:
        kwargs['user'] = exc.default

    try:
        kwargs['confirmation'] = svc.conf_get(s, 'confirmation')
    except ex.OptNotFound as exc:
        kwargs['confirmation'] = exc.default

    import resTask
    r = resTask.Task(**kwargs)
    svc += r

def add_app(svc, s):
    resApp = ximport('resApp')
    kwargs = init_kwargs(svc, s)

    try:
        kwargs['script'] = svc.conf_get(s, 'script')
    except ex.OptNotFound:
        pass

    try:
        kwargs['start'] = svc.conf_get(s, 'start')
    except ex.OptNotFound as exc:
        kwargs['start'] = exc.default

    try:
        kwargs['stop'] = svc.conf_get(s, 'stop')
    except ex.OptNotFound as exc:
        kwargs['stop'] = exc.default

    try:
        kwargs['check'] = svc.conf_get(s, 'check')
    except ex.OptNotFound as exc:
        kwargs['check'] = exc.default

    try:
        kwargs['info'] = svc.conf_get(s, 'info')
    except ex.OptNotFound as exc:
        kwargs['info'] = exc.default

    try:
        kwargs['timeout'] = svc.conf_get(s, 'timeout')
    except ex.OptNotFound as exc:
        kwargs['timeout'] = exc.default

    try:
        kwargs['start_timeout'] = svc.conf_get(s, 'start_timeout')
    except ex.OptNotFound as exc:
        kwargs['start_timeout'] = exc.default

    try:
        kwargs['stop_timeout'] = svc.conf_get(s, 'stop_timeout')
    except ex.OptNotFound as exc:
        kwargs['stop_timeout'] = exc.default

    try:
        kwargs['check_timeout'] = svc.conf_get(s, 'check_timeout')
    except ex.OptNotFound as exc:
        kwargs['check_timeout'] = exc.default

    try:
        kwargs['info_timeout'] = svc.conf_get(s, 'info_timeout')
    except ex.OptNotFound as exc:
        kwargs['info_timeout'] = exc.default

    r = resApp.App(**kwargs)
    svc += r


def setup_logging(svcnames):
    """Setup logging to stream + logfile, and logfile rotation
    class Logger instance name: 'log'
    """
    max_svcname_len = 0

    # compute max svcname length to align logging stream output
    for svcname in svcnames:
        n = len(svcname)
        if n > max_svcname_len:
            max_svcname_len = n

    rcLogger.max_svcname_len = max_svcname_len
    rcLogger.initLogger(rcEnv.nodename)

def build(name, minimal=False, svcconf=None, node=None):
    """build(name) is in charge of Svc creation
    it return None if service Name is not managed by local node
    else it return new Svc instance
    """
    import svc

    #
    # node discovery is hidden in a separate module to
    # keep it separate from the framework stuff
    #
    discover_node()
    svc = svc.Svc(svcname=name, node=node, cf=svcconf)

    try:
        encapnodes = svc.conf_get('DEFAULT', "encapnodes")
    except ex.OptNotFound as exc:
        encapnodes = exc.default
    svc.encapnodes = set(encapnodes)

    try:
        nodes = svc.conf_get('DEFAULT', "nodes")
    except ex.OptNotFound as exc:
        nodes = exc.default
    svc.ordered_nodes = nodes
    svc.nodes = set(nodes)

    try:
        drpnodes = svc.conf_get('DEFAULT', "drpnodes")
    except ex.OptNotFound as exc:
        drpnodes = exc.default

    try:
        drpnode = svc.conf_get('DEFAULT', "drpnode").lower()
        if drpnode not in drpnodes and drpnode != "":
            drpnodes.append(drpnode)
    except ex.OptNotFound as exc:
        drpnode = ''
    svc.ordered_drpnodes = drpnodes
    svc.drpnodes = set(drpnodes)

    try:
        flex_primary = svc.conf_get('DEFAULT', "flex_primary").lower()
    except ex.OptNotFound as exc:
        if len(nodes) > 0:
            flex_primary = nodes[0]
        else:
            flex_primary = ''
    svc.flex_primary = flex_primary

    try:
        drp_flex_primary = svc.conf_get('DEFAULT', "drp_flex_primary").lower()
    except ex.OptNotFound as exc:
        if len(drpnodes) > 0:
            drp_flex_primary = drpnodes[0]
        else:
            drp_flex_primary = ''
    svc.drp_flex_primary = drp_flex_primary

    try:
        svc.parents = svc.conf_get('DEFAULT', "parents")
    except ex.OptNotFound as exc:
        svc.parents = exc.default

    try:
        svc.placement = svc.conf_get('DEFAULT', "placement")
    except ex.OptNotFound as exc:
        svc.placement = exc.default

    try:
        svc.orchestrate = svc.conf_get("DEFAULT", "orchestrate")
    except ex.OptNotFound as exc:
        svc.orchestrate = exc.default

    try:
        svc.stonith = svc.conf_get("DEFAULT", "stonith")
    except ex.OptNotFound as exc:
        svc.stonith = exc.default


    #
    # Store and validate the service type
    #
    if svc.conf_has_option_scoped("DEFAULT", "env"):
        svc.svc_env = svc.conf_get('DEFAULT', "env")
    elif svc.conf_has_option_scoped("DEFAULT", "service_type"):
        svc.svc_env = svc.conf_get('DEFAULT', "service_type")

    try:
        svc.lock_timeout = svc.conf_get('DEFAULT', 'lock_timeout')
    except ex.OptNotFound as exc:
        pass

    try:
        svc.presnap_trigger = svc.conf_get('DEFAULT', 'presnap_trigger')
    except ex.OptNotFound as exc:
        pass

    try:
        svc.postsnap_trigger = svc.conf_get('DEFAULT', 'postsnap_trigger')
    except ex.OptNotFound as exc:
        pass

    try:
        svc.disable_rollback = not svc.conf_get('DEFAULT', "rollback")
    except ex.OptNotFound as exc:
        pass

    svc.encap = rcEnv.nodename in svc.encapnodes

    #
    # amazon options
    #
    try:
        svc.aws = svc.conf_get("DEFAULT", 'aws')
    except ex.OptNotFound as exc:
        pass

    try:
        svc.aws_profile = svc.conf_get("DEFAULT", 'aws_profile')
    except ex.OptNotFound as exc:
        pass

    #
    # process group options
    #
    try:
        svc.create_pg = svc.conf_get("DEFAULT", 'create_pg')
    except ex.OptNotFound as exc:
        pass

    try:
        svc.topology = svc.conf_get('DEFAULT', 'topology')
    except ex.OptNotFound as exc:
        pass

    try:
        svc.show_disabled = svc.conf_get('DEFAULT', 'show_disabled')
    except ex.OptNotFound as exc:
        svc.show_disabled = True

    try:
        svc.comment = svc.conf_get('DEFAULT', 'comment')
    except ex.OptNotFound as exc:
        pass

    try:
        svc.monitor_action = svc.conf_get('DEFAULT', "monitor_action")
    except ex.OptNotFound as exc:
        pass

    try:
        svc.pre_monitor_action = svc.conf_get('DEFAULT', "pre_monitor_action")
    except ex.OptNotFound as exc:
        pass

    try:
        svc.bwlimit = svc.conf_get('DEFAULT', "bwlimit")
    except ex.OptNotFound as exc:
        svc.bwlimit = None

    if minimal:
        svc.options.minimal = True
        return svc
    if svc.scale_target is not None:
        return svc

    svc.options.minimal = False

    #
    # instanciate resources
    #
    add_resources(svc, 'container')
    add_resources(svc, 'ip')
    add_resources(svc, 'disk')
    add_resources(svc, 'fs')
    add_resources(svc, 'share')
    add_resources(svc, 'app')
    add_resources(svc, 'task')

    # deprecated, folded into "disk"
    add_resources(svc, 'vdisk')
    add_resources(svc, 'vmdg')
    add_resources(svc, 'loop')
    add_resources(svc, 'drbd')
    add_resources(svc, 'vg')
    add_resources(svc, 'pool')

    add_resources(svc, 'sync')
    add_mandatory_syncs(svc)

    svc.post_build()
    return svc

def is_service(f):
    if os.name == 'nt':
        return True
    if os.path.realpath(f) != os.path.realpath(rcEnv.paths.svcmgr):
        return False
    if not os.path.exists(f + '.conf'):
        return False
    return True

def list_services():
    if not os.path.exists(rcEnv.paths.pathetc):
        print("create dir %s"%rcEnv.paths.pathetc)
        os.makedirs(rcEnv.paths.pathetc)

    s = glob.glob(os.path.join(rcEnv.paths.pathetc, '*.conf'))
    s = [os.path.basename(x)[:-5] for x in s]

    l = []
    for name in s:
        if len(s) == 0:
            continue
        if not is_service(os.path.join(rcEnv.paths.pathetc, name)):
            continue
        l.append(name)
    return l

def build_services(status=None, svcnames=None, create_instance=False,
                   minimal=False, node=None):
    """
    Returns a list of all services of status matching the specified status.
    If no status is specified, returns all services.
    """
    import svc

    if svcnames is None:
        svcnames = []

    check_privs()

    errors = []
    services = {}

    if isinstance(svcnames, str):
        svcnames = [svcnames]

    if len(svcnames) == 0:
        svcnames = list_services()
        missing_svcnames = []
    else:
        all_svcnames = list_services()
        missing_svcnames = sorted(list(set(svcnames) - set(all_svcnames)))
        for m in missing_svcnames:
            if create_instance:
                services[m] = svc.Svc(m, node=node)
            else:
                errors.append("%s: service does not exist" % m)
        svcnames = list(set(svcnames) & set(all_svcnames))

    setup_logging(svcnames)

    for name in svcnames:
        try:
            svc = build(name, minimal=minimal, node=node)
        except (ex.excError, ex.excInitError, ValueError) as e:
            errors.append("%s: %s" % (name, str(e)))
            svclog = rcLogger.initLogger(rcEnv.nodename+"."+name, handlers=["file", "syslog"])
            svclog.error(str(e))
            continue
        except ex.excAbortAction:
            continue
        except:
            import traceback
            traceback.print_exc()
            continue
        services[svc.svcname] = svc
    return [s for _, s in sorted(services.items())], errors

def create(svcname, resources=[], provision=False):
    if not isinstance(svcname, list):
        print("ouch, svcname should be a list object", file=sys.stderr)
        return {"ret": 1}
    if len(svcname) != 1:
        print("you must specify a single service name with the 'create' action", file=sys.stderr)
        return {"ret": 1}
    svcname = svcname[0]
    if len(svcname) == 0:
        print("service name must not be empty", file=sys.stderr)
        return {"ret": 1}
    if svcname in list_services():
        print("service", svcname, "already exists", file=sys.stderr)
        return {"ret": 1}
    cf = os.path.join(rcEnv.paths.pathetc, svcname+'.conf')
    if os.path.exists(cf):
        import shutil
        print(cf, "already exists. save as "+svcname+".conf.bak", file=sys.stderr)
        shutil.move(cf, os.path.join(rcEnv.paths.pathtmp, svcname+".conf.bak"))
    try:
        f = open(cf, 'w')
    except:
        print("failed to open", cf, "for writing", file=sys.stderr)
        return {"ret": 1}

    defaults = {}
    sections = {}
    rtypes = {}

    import json
    for r in resources:
        try:
            d = json.loads(r)
        except:
            print("can not parse resource:", r, file=sys.stderr)
            return {"ret": 1}
        if 'rid' in d:
            section = d['rid']
            if '#' not in section:
                print(section, "must be formatted as 'rtype#n'", file=sys.stderr)
                return {"ret": 1}
            l = section.split('#')
            if len(l) != 2:
                print(section, "must be formatted as 'rtype#n'", file=sys.stderr)
                return {"ret": 1}
            rtype = l[1]
            if rtype in rtypes:
                rtypes[rtype] += 1
            else:
                rtypes[rtype] = 0
            del d['rid']
            if section in sections:
                sections[section].update(d)
            else:
                sections[section] = d
        elif 'rtype' in d and d["rtype"] == "env":
            del d["rtype"]
            if "env" in sections:
                sections["env"].update(d)
            else:
                sections["env"] = d
        elif 'rtype' in d and d["rtype"] != "DEFAULT":
            if 'rid' in d:
                del d['rid']
            rtype = d['rtype']
            if rtype in rtypes:
                section = '%s#%d'%(rtype, rtypes[rtype])
                rtypes[rtype] += 1
            else:
                section = '%s#0'%rtype
                rtypes[rtype] = 1
            if section in sections:
                sections[section].update(d)
            else:
                sections[section] = d
        else:
            if "rtype" in d:
                del d["rtype"]
            defaults.update(d)

    from svcdict import KeyDict
    from keywords import MissKeyNoDefault, KeyInvalidValue
    try:
        keys = KeyDict(provision=provision)
        defaults.update(keys.update('DEFAULT', defaults))
        for section, d in sections.items():
            sections[section].update(keys.update(section, d))
    except (MissKeyNoDefault, KeyInvalidValue):
        return {"ret": 1}

    conf = rcConfigParser.RawConfigParser(defaults)
    for section, d in sections.items():
        conf.add_section(section)
        for key, val in d.items():
            if key == 'rtype':
                continue
            conf.set(section, key, val)

    from freezer import Freezer
    Freezer(svcname).freeze()

    conf.write(f)

    initdir = svcname+'.dir'
    svcinitdir = os.path.join(rcEnv.paths.pathetc, initdir)
    if not os.path.exists(svcinitdir):
        os.makedirs(svcinitdir)
    fix_app_link(svcname)
    fix_exe_link(svcname)
    return {"ret": 0, "rid": sections.keys()}

def fix_app_link(svcname):
    """
    Create the <svcname>.d -> <svcname>.dir symlink
    """
    if os.name != 'posix':
        return
    os.chdir(rcEnv.paths.pathetc)
    src = svcname+'.d'
    dst = svcname+'.dir'
    try:
        os.readlink(src)
    except:
        if not os.path.exists(dst):
            os.makedirs(dst)
        os.symlink(dst, src)

def fix_exe_link(svcname):
    """
    Create the <svcname> -> svcmgr symlink
    """
    if os.name != 'posix':
        return
    os.chdir(rcEnv.paths.pathetc)
    try:
        p = os.readlink(svcname)
    except:
        os.symlink(rcEnv.paths.svcmgr, svcname)
        p = rcEnv.paths.svcmgr
    if p != rcEnv.paths.svcmgr:
        os.unlink(svcname)
        os.symlink(rcEnv.paths.svcmgr, svcname)
  0707010005ca11000081b40000000000000000000000015ab217540002d0ae000000660000000000000000000000000000002600000000root/usr/share/opensvc/lib/svcdict.py import sys
from rcGlobalEnv import rcEnv
from rcNode import node_get_node_env
from keywords import Keyword, KeywordStore

# deprecated => supported
DEPRECATED_KEYWORDS = {
  "DEFAULT.mode": None,
  "DEFAULT.cluster_type": "topology",
  "DEFAULT.service_type": "env",
  "DEFAULT.affinity": "hard_affinity",
  "DEFAULT.anti_affinity": "hard_anti_affinity",
  "disk.lvm.vgname": "name",
  "disk.pool.poolname": "name",
  "disk.vg.vgname": "name",
  "sync.rsync.exclude": "options",
  "disk.zpool.poolname": "name",
  "always_on": None,
}

# supported => deprecated
REVERSE_DEPRECATED_KEYWORDS = {
  "DEFAULT.topology": "cluster_type",
  "DEFAULT.env": "service_type",
  "DEFAULT.hard_affinity": "affinity",
  "DEFAULT.hard_anti_affinity": "anti_affinity",
  "disk.lvm.name": "vgname",
  "disk.pool.name": "poolname",
  "disk.vg.name": "vgname",
  "sync.rsync.options": "exclude",
  "disk.zpool.name": "poolname",
}

DEPRECATED_SECTIONS = {
  "disk.pool": ["disk", "zpool"],
  "drbd": ["disk", "drbd"],
  "loop": ["disk", "loop"],
  "pool": ["disk", "zpool"],
  "vdisk": ["disk", "vdisk"],
  "vmdg": ["disk", "vmdg"],
  "vg": ["disk", "vg"],
}


class KeywordLockTimeout(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="lock_timeout",
                  order=10,
                  default=60,
                  convert="duration",
                  text="A duration expression, like '1m30s'. The maximum wait time for the action lock acquire. The svcmgr --waitlock option overrides this parameter."
                )

class KeywordMode(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="mode",
                  order=10,
                  default="hosted",
                  candidates=["hosted"],
                  text="Deprecated. The value is always 'hosted'. The keyword is kept around for now the ease transition from older agents."
                )

class KeywordRollback(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="rollback",
                  at=True,
                  order=11,
                  default=True,
                  convert="boolean",
                  text="If set to False, the default rollback on action error is inhibited, leaving the service in its half-started state."
                )

class KeywordCompSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="comp_schedule",
                  at=True,
                  order=11,
                  default="00:00-06:00@361",
                  text="The service compliance run schedule. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordStatusSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="status_schedule",
                  at=True,
                  order=11,
                  default="@10",
                  text="The service status evaluation schedule. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordDefaultSyncSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="sync_schedule",
                  at=True,
                  order=11,
                  default="04:00-06:00@121",
                  text="The default sync resources schedule. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordDefaultAws(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="aws",
                  at=True,
                  order=11,
                  text="The aws cli executable fullpath. If not provided, aws is expected to be found in the PATH."
                )

class KeywordDefaultAwsProfile(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="aws_profile",
                  at=True,
                  default="default",
                  order=11,
                  text="The profile to use with the AWS api."
                )

class KeywordResinfoSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="resinfo_schedule",
                  at=True,
                  order=11,
                  default="@60",
                  text="The service resource info push schedule. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordMonitorSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="monitor_schedule",
                  at=True,
                  order=11,
                  default="@1",
                  text="The service resource monitor schedule. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordPushSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="push_schedule",
                  at=True,
                  order=11,
                  default="00:00-06:00@361",
                  text="The service configuration emission to the collector schedule. See usr/share/doc/schedule for the schedule syntax."
                )

class KeywordFlexPrimary(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="flex_primary",
                  at=True,
                  order=11,
                  depends=[('topology', ["flex"])],
                  default_text="<first node of the nodes parameter>",
                  text="The node in charge of syncing the other nodes. --cluster actions on the flex_primary are execute on all peer nodes (ie, not drpnodes)."
                )

class KeywordDrpFlexPrimary(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="drp_flex_primary",
                  at=True,
                  order=11,
                  depends=[('topology', ["flex"])],
                  default_text="<first node of the drpnodes parameter>",
                  text="The drpnode in charge of syncing the other drpnodes. --cluster actions on the drp_flex_primary are execute on all drpnodes (ie, not pri nodes)."
                )

class KeywordDockerSwarmManagers(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="docker_swarm_managers",
                  convert="list",
                  default=[],
                  order=20,
                  at=True,
                  text="List of nodes promoted as docker swarm managers.The flex primary node is implicitely a manager. Whitespace separated."
                )

class KeywordDockerExe(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="docker_exe",
                  at=True,
                  order=12,
                  text="If you have multiple docker versions installed and want the service to stick to a version whatever the PATH definition, you should set this parameter to the full path to the docker executable.",
                  example="/usr/bin/docker-1.8"
                )

class KeywordDockerdExe(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="dockerd_exe",
                  at=True,
                  order=12,
                  text="If you have multiple docker versions installed and want the service to stick to a version whatever the PATH definition, you should set this parameter to the full path to the docker daemon executable.",
                  example="/usr/bin/dockerd-1.8"
                )

class KeywordDockerDataDir(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="docker_data_dir",
                  at=True,
                  order=12,
                  text="If the service has docker-type container resources and this keyword is set, the service starts a service-private docker daemon. Its socket is <pathvar>/services/<svcname>/docker.sock, and its data directory is specified by this keyword. This organization is necessary to enable stateful service relocalization.",
                  example="/srv/svc1/data/docker"
                )

class KeywordDockerDaemonPrivate(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="docker_daemon_private",
                  at=True,
                  default_text="<true on Linux, else False>",
                  convert="boolean",
                  order=11,
                  text="If set to False, this service will use the system's shared docker daemon instance. This is parameter is forced to False on non-Linux systems.",
                  example="True"
                )

class KeywordDockerDaemonArgs(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="docker_daemon_args",
                  at=True,
                  order=12,
                  convert="shlex",
                  default=[],
                  text="If the service has docker-type container resources, the service handles the startup of a private docker daemon. OpenSVC sets the socket and data dir parameters. Admins can set extra parameters using this keyword. For example, it can be useful to set the --ip parameter for a docker registry service.",
                  example="--ip 1.2.3.4"
                )

class KeywordDockerSwarmArgs(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="docker_swarm_args",
                  at=True,
                  order=12,
                  convert="shlex",
                  default=[],
                  text="The arguments passed to docker swarm init on the flex primary, and to docker swarm join on the the other nodes. The --token argument must not be specified, as it is handled by the agent. Scoping this parameter permits to set additional parameters on the flex_primary for use with swarm init only, like --autolock.",
                  example="--advertize-addr {ip#0.ipname} --listen-addr {ip#0.ipname}",
                )

class KeywordSubsetParallel(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="subset",
                  keyword="parallel",
                  at=True,
                  candidates=(True, False),
                  default=False,
                  convert="boolean",
                  text="If set to true, actions are executed in parallel amongst the subset member resources.",
                  order=2
                )

class KeywordContainerType(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="type",
                  at=True,
                  candidates=rcEnv.vt_supported,
                  text="The type of container.",
                  required=True,
                  order=1
                )

class KeywordContainerZoneDeleteOnStop(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  rtype="zone",
                  keyword="delete_on_stop",
                  at=True,
                  candidates=(True, False),
                  text="If set to true, the zone configuration is deleted after a resource stop. The agent maintains an export of the configuration for the next start. This export is replicated to the other nodes and drp nodes so they can take over the zone even if it is completely hosted on a shared disk.",
                  default=False,
                  convert="boolean",
                  order=1
                )

class KeywordDockerDockerService(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="docker_service",
                  at=True,
                  order=9,
                  rtype="docker",
                  default=False,
                  convert="boolean",
                  candidates=(True, False),
                  text="If set to True, run this container as a docker service, which is possible if the :kw:`topology` is set to flex and the docker swarm properly initialized.",
                  example=False
                )

class KeywordDockerRunImage(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="run_image",
                  at=True,
                  required=True,
                  order=9,
                  rtype="docker",
                  text="The docker image pull, and run the container with.",
                  example="83f2a3dd2980"
                )

class KeywordDockerRunCommand(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="run_command",
                  at=True,
                  order=1,
                  rtype="docker",
                  text="The command to execute in the docker container on run.",
                  example="/opt/tomcat/bin/catalina.sh"
                )

class KeywordDockerRunArgs(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="run_args",
                  at=True,
                  order=2,
                  rtype="docker",
                  text="Extra arguments to pass to the docker run command, like volume and port mappings.",
                  example="-v /opt/docker.opensvc.com/vol1:/vol1:rw -p 37.59.71.25:8080:8080"
                )

class KeywordVirtinst(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="virtinst",
                  rtype=["kvm", "xen", "ovm"],
                  text="The virt-install command to use to create the container.",
                  required=True,
                  provisioning=True
                )

class KeywordSnap(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="snap",
                  rtype=["kvm", "xen", "ovm", "zone", "esx"],
                  text="The target snapshot/clone full path containing the new container disk files.",
                  required=True,
                  provisioning=True
                )

class KeywordSnapof(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="snapof",
                  rtype=["kvm", "xen", "ovm", "zone", "esx"],
                  text="The snapshot origin full path containing the reference container disk files.",
                  required=True,
                  provisioning=True
                )

class KeywordContainerOrigin(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="container_origin",
                  rtype="zone",
                  text="The origin container having the reference container disk files.",
                  provisioning=True
                )

class KeywordJailRoot(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="jailroot",
                  rtype="jail",
                  text="Sets the root fs directory of the container",
                  required=True,
                )

class KeywordContainerLxdLaunchImage(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="launch_image",
                  at=True,
                  required=True,
                  order=9,
                  rtype="lxd",
                  provisioning=True,
                  text="The lxd image to instanciate on provision.",
                  example="ubuntu:16.04"
                )

class KeywordContainerLxdLaunchOptions(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="launch_options",
                  at=True,
                  order=9,
                  rtype="lxd",
                  provisioning=True,
                  convert="shlex",
                  default=[],
                  default_text="",
                  text="The :cmd:``lxc launch <image> ... <name>`` options set on provision.",
                  example="-p default"
                )

class KeywordLxcCf(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="cf",
                  rtype="lxc",
                  text="Defines a lxc configuration file in a non-standard location.",
                  provisioning=True,
                  example="/srv/mycontainer/config"
                )

class KeywordRootfs(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="rootfs",
                  rtype=["lxc", "vz", "zone"],
                  text="Sets the root fs directory of the container",
                  required=True,
                  provisioning=True
                )

class KeywordTemplate(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="template",
                  rtype=["lxc", "vz", "zone"],
                  text="Sets the url of the template unpacked into the container root fs.",
                  required=True,
                  provisioning=True
                )

class KeywordVmName(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="name",
                  at=True,
                  order=2,
                  rtype=rcEnv.vt_supported,
                  default_text="the service name",
                  text="Set if the container hostname is different from the container name."
                )

class KeywordVmHostname(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="hostname",
                  at=True,
                  order=2,
                  rtype=rcEnv.vt_supported,
                  text="This need to be set if the virtual machine hostname is different from the machine name."
                )

class KeywordContainerRcmd(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="rcmd",
                  convert="shlex",
                  at=True,
                  order=2,
                  rtype="lxc",
                  example="lxc-attach -e -n osvtavnprov01 -- ",
                  text="An container remote command override the agent default"
                )

class KeywordContainerVapp(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="vapp",
                  rtype="vcloud",
                  required=True,
                  at=True,
                  order=2,
                  example="MyVapp",
                  text="The Vcloud Virtual App hosting the VM."
                )

class KeywordOsvcRootPath(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="osvc_root_path",
                  at=True,
                  order=2,
                  rtype=rcEnv.vt_supported,
                  example="/opt/opensvc",
                  text="If the OpenSVC agent is installed via package in the container, this parameter must not be set or set to 'None'. Else the value can be set to the fullpath hosting the agent installed from sources."
                )

class KeywordGuestos(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="guestos",
                  at=True,
                  rtype=rcEnv.vt_supported,
                  order=11,
                  candidates=["unix", "windows"],
                  text="The operating system in the virtual machine."
                )

class KeywordJailIps(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="ips",
                  convert="list",
                  at=True,
                  rtype="jail",
                  order=11,
                  text="The ipv4 addresses of the jail."
                )

class KeywordJailIp6s(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="ip6s",
                  convert="list",
                  at=True,
                  rtype="jail",
                  order=11,
                  text="The ipv6 addresses of the jail."
                )

class KeywordSharedIpGroup(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="shared_ip_group",
                  at=True,
                  order=11,
                  rtype=rcEnv.vt_cloud,
                  text="The cloud shared ip group name to allocate a public ip from."
                )

class KeywordSize(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="size",
                  at=True,
                  order=11,
                  rtype=rcEnv.vt_cloud,
                  text="The cloud vm size, as known to the cloud manager. Example: tiny."
                )

class KeywordKeyName(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="key_name",
                  at=True,
                  required=True,
                  order=11,
                  rtype=rcEnv.vt_cloud,
                  text="The key name, as known to the cloud manager, to trust in the provisioned vm."
                )

class KeywordSrpPrmCores(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="prm_cores",
                  order=11,
                  rtype="srp",
                  default=1,
                  convert="integer",
                  provisioning=True,
                  text="The number of core to bind the SRP container to."
                )

class KeywordSrpIp(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="ip",
                  at=True,
                  order=11,
                  rtype="srp",
                  provisioning=True,
                  text="The ip name or addr used to create the SRP container."
                )

class KeywordSrpRootpath(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="rootpath",
                  at=True,
                  order=11,
                  rtype="srp",
                  provisioning=True,
                  text="The path of the SRP container root filesystem."
                )

class KeywordCloudId(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="cloud_id",
                  required=True,
                  at=True,
                  order=11,
                  rtype=rcEnv.vt_cloud,
                  text="The cloud id as configured in node.conf. Example: cloud#1."
                )

class KeywordVmUuid(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="uuid",
                  at=True,
                  required=True,
                  order=11,
                  rtype="ovm",
                  text="The virtual machine unique identifier used to pass commands on the VM."
                )

class KeywordHardAffinity(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="hard_affinity",
                  convert="set",
                  default=set(),
                  at=True,
                  order=15,
                  text="A whitespace separated list of services that must be started on the node to allow the monitor to start this service.",
                  example="svc1 svc2"
                )

class KeywordHardAntiAffinity(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="hard_anti_affinity",
                  convert="set",
                  default=set(),
                  at=True,
                  order=15,
                  text="A whitespace separated list of services that must not be started on the node to allow the monitor to start this service.",
                  example="svc1 svc2"
                )

class KeywordSoftAffinity(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="soft_affinity",
                  convert="set",
                  default=set(),
                  at=True,
                  order=15,
                  text="A whitespace separated list of services that must be started on the node to allow the monitor to start this service. If the local node is the only candidate ignore this constraint and allow start.",
                  example="svc1 svc2"
                )

class KeywordSoftAntiAffinity(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="soft_anti_affinity",
                  convert="set",
                  default=set(),
                  at=True,
                  order=15,
                  text="A whitespace separated list of services that must not be started on the node to allow the monitor to start this service. If the local node is the only candidate ignore this constraint and allow start.",
                  example="svc1 svc2"
                )

class KeywordPrKey(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="prkey",
                  order=15,
                  at=True,
                  text="Defines a specific default persistent reservation key for the service. A prkey set in a resource takes priority. If no prkey is specified in the service nor in the DEFAULT section, the prkey in node.conf is used. If node.conf has no prkey set, the hostid is computed and written in node.conf."
                )

class KeywordDefaultNoPreemptAbort(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="no_preempt_abort",
                  order=15,
                  at=True,
                  candidates=(True, False),
                  default=False,
                  convert="boolean",
                  text="If set to 'true', OpenSVC will preempt scsi reservation with a preempt command instead of a preempt and and abort. Some scsi target implementations do not support this last mode (esx). If set to 'false' or not set, 'no_preempt_abort' can be activated on a per-resource basis."
                )

class KeywordShowDisabled(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="show_disabled",
                  at=True,
                  order=15,
                  default=True,
                  convert="boolean",
                  candidates=[True, False],
                  text="Specifies if the disabled resources must be included in the print status and json status output."
                )

class KeywordTopology(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="topology",
                  at=True,
                  order=15,
                  default="failover",
                  candidates=["failover", "flex"],
                  text="failover: the service is allowed to be up on one node at a time. flex: the service can be up on n out of m nodes (n <= m), n/m must be in the [flex_min_nodes, flex_max_nodes] range."
                )

class KeywordScale(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="scale",
                  at=True,
                  order=15,
                  convert="integer",
                  text="If set, create and provision the necessary slave services, named <n>.<svcname>, to meet the target <scale> number of started instances."
                )

class KeywordScalerSlave(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="scaler_slave",
                  convert="boolean",
                  default=False,
                  at=True,
                  order=15,
                  text="Automatically set to true by the daemon monitor when creating new scaler slaves."
                )

class KeywordOrchestrate(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="orchestrate",
                  at=True,
                  order=16,
                  default="no",
                  convert="string",
                  candidates=("ha", "start", "no"),
                  text="If set to 'no', disable service orchestration by the OpenSVC daemon monitor, including service start on boot. If set to 'start' failover services won't failover automatically, though the service instance on the natural placement leader is started if another instance is not already up. Flex services won't start missing instances to meet the flex_min_nodes target, though the <flex_min_nodes>th instances on best placement leaders are started if the instances minimum quota is not already reached. Resource restart is still active whatever the orchestrate value.",
                )

class KeywordStonith(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="stonith",
                  convert="boolean",
                  order=16,
                  default=False,
                  candidates=(True, False),
                  depends=[("topology", ["failover"])],
                  text="Stonith the node previously running the service if stale upon start by the daemon monitor.",
                )

class KeywordPlacement(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="placement",
                  order=16,
                  default="nodes order",
                  candidates=["nodes order", "load avg", "shift", "spread", "score"],
                  text="Set a service instances placement policy:\n\n"
                       "* nodes order: the left-most available node is allowed to start a service instance when necessary.\n"
                       "* load avg: the least loaded node takes precedences.\n"
                       "* shift: shift the nodes order ranking by the service prefix converter to an integer.\n"
                       "* spread: a spread policy tends to perfect leveling with many services.\n"
                       "* score: the highest scoring node takes precedence (the score is a composite indice of load, mem and swap).\n",
                )

class KeywordConstraints(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="constraints",
                  at=True,
                  depends=[("orchestrate", "ha")],
                  order=16,
                  example="$(\"{nodename}\"==\"n2.opensvc.com\")",
                  text="An expression evaluating as a boolean, constraining the service instance placement by the daemon monitor to nodes with the constraints evaluated as True.\n\nThe constraints are not honored by manual start operations. The constraints value is embedded in the json status.\n\nSupported comparison operators are ``==``, ``!=``, ``>``, ``>=``, ``<=``, ``in (e1, e2)``, ``in [e1, e2]``.\n\nSupported arithmetic operators are ``*``, ``+``, ``-``, ``/``, ``**``, ``//``, ``%``.\n\nSupported binary operators are ``&``, ``|``, ``^``.\n\nThe negation operator is ``not``.\n\nSupported boolean operators are ``and``, ``or``.\n\nReferences are allowed.\n\nStrings, and references evaluating as strings, containing dots must be quoted.",
                )

class KeywordFlexMinNodes(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="flex_min_nodes",
                  order=16,
                  default=1,
                  convert="integer",
                  depends=[("topology", ["flex"])],
                  text="Minimum number of active nodes in the cluster. Below this number alerts are raised by the collector, and the collector won't stop any more service instances."
                )

class KeywordFlexMaxNodes(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="flex_max_nodes",
                  order=16,
                  default_text="<number of peer nodes>",
                  convert="integer",
                  depends=[("topology", ["flex"])],
                  text="Maximum number of active nodes in the cluster. Above this number alerts are raised by the collector, and the collector won't start any more service instances. 0 means unlimited."
                )

class KeywordFlexCpuLowThreshold(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="flex_cpu_low_threshold",
                  order=16,
                  default=0,
                  convert="integer",
                  depends=[("topology", ["flex"])],
                  text="Cluster-wide load average below which flex service instances will be stopped.",
                )

class KeywordFlexCpuHighThreshold(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="flex_cpu_high_threshold",
                  order=16,
                  default=100,
                  convert="integer",
                  depends=[("topology", ["flex"])],
                  text="Cluster-wide load average above which flex new service instances will be started.",
                )

class KeywordServiceEnv(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="env",
                  order=15,
                  default=node_get_node_env(),
                  default_text="<same as node env>",
                  candidates=rcEnv.allowed_svc_envs,
                  text="A non-PRD service can not be brought up on a PRD node, but a PRD service can be startup on a non-PRD node (in a DRP situation). The default value is the node env."
                )

class KeywordParents(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="parents",
                  order=20,
                  at=True,
                  default=[],
                  default_text="",
                  convert="list_lower",
                  text="List of services that must be 'avail up' before allowing this service to be started by the daemon monitor. Whitespace separated."
                )

class KeywordChildren(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="children",
                  order=20,
                  at=True,
                  default=[],
                  default_text="",
                  convert="list_lower",
                  text="List of services that must be 'avail down' before allowing this service to be stopped by the daemon monitor. Whitespace separated."
                )

class KeywordSlaves(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="slaves",
                  order=20,
                  at=True,
                  default=[],
                  convert="list",
                  text="List of services to propagate the start and stop actions to."
                )

class KeywordNodes(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="nodes",
                  order=20,
                  at=True,
                  default=[rcEnv.nodename],
                  default_text="<hostname of the current node>",
                  convert="list_lower",
                  text="List of cluster local nodes able to start the service.  Whitespace separated."
                )

class KeywordDrpnode(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="drpnode",
                  order=21,
                  at=True,
                  text="The backup node where the service is activated in a DRP situation. This node is also a data synchronization target for 'sync' resources.",
                  example="node1"
                )

class KeywordDrpnodes(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="drpnodes",
                  order=21,
                  at=True,
                  convert="list_lower",
                  default=[],
                  default_text="",
                  text="Alternate backup nodes, where the service could be activated in a DRP situation if the 'drpnode' is not available. These nodes are also data synchronization targets for 'sync' resources.",
                  example="node1 node2"
                )

class KeywordEncapnodes(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="encapnodes",
                  order=21,
                  convert="list_lower",
                  default=[],
                  default_text="",
                  text="The list of containers handled by this service and with an OpenSVC agent installed to handle the encapsulated resources. With this parameter set, parameters can be scoped with the @encapnodes suffix.",
                  example="vm1 vm2"
                )

class KeywordApp(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="app",
                  order=24,
                  default="default",
                  text="Used to identify who is responsible for this service, who is billable and provides a most useful filtering key. Better keep it a short code."
                )

class KeywordComment(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="comment",
                  order=25,
                  text="Helps users understand the role of the service, which is nice to on-call support people having to operate on a service they are not usually responsible for."
                )

class KeywordDefaultScsireserv(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="scsireserv",
                  at=True,
                  order=25,
                  default=False,
                  convert="boolean",
                  candidates=(True, False),
                  text="If set to 'true', OpenSVC will try to acquire a type-5 (write exclusive, registrant only) scsi3 persistent reservation on every path to disks of every disk group attached to this service. Existing reservations are preempted to not block service start-up. If the start-up was not legitimate the data are still protected from being written over from both nodes. If set to 'false' or not set, 'scsireserv' can be activated on a per-resource basis."
                )

class KeywordBwlimit(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="bwlimit",
                  convert="speed_kps",
                  order=25,
                  text="Bandwidth limit in KB applied to all rsync transfers. Leave empty to enforce no limit.",
                  example="3 mb/s"
                )

class KeywordSyncInterval(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="sync_interval",
                  order=26,
                  default=121,
                  convert="duration",
                  text="Set the minimum delay between syncs in minutes. If a sync is triggered through a scheduler or manually, it is skipped if last sync occurred less than 'sync_min_delay' ago. The mecanism is enforced by a timestamp created upon each sync completion in <pathvar>/services/<svcname>/<rid>/last_sync_<node>"
                )

class KeywordSyncMaxDelay(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="sync_max_delay",
                  order=27,
                  default=1440,
                  convert="duration",
                  text="Unit is minutes. This sets to delay above which the sync status of the resource is to be considered down. Should be set according to your application service level agreement. The scheduler task frequency should be set between 'sync_min_delay' and 'sync_max_delay'"
                )

class KeywordPresnapTrigger(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="presnap_trigger",
                  convert="shlex",
                  order=28,
                  text="Define a command to run before creating snapshots. This is most likely what you need to use plug a script to put you data in a coherent state (alter begin backup and the like).",
                  example="/srv/svc1/etc/init.d/pre_snap.sh"
                )

class KeywordPostsnapTrigger(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="postsnap_trigger",
                  convert="shlex",
                  order=29,
                  text="Define a command to run after snapshots are created. This is most likely what you need to use plug a script to undo the actions of 'presnap_trigger'.",
                  example="/srv/svc1/etc/init.d/post_snap.sh"
                )

class KeywordMonitorAction(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="monitor_action",
                  at=True,
                  order=30,
                  candidates=("reboot", "crash", "freezestop"),
                  text="The action to take when a monitored resource is not up nor standby up, and if the resource restart procedure has failed.",
                  example="reboot"
                )

class KeywordPreMonitorAction(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="pre_monitor_action",
                  at=True,
                  order=30,
                  text="A script to execute before the monitor_action. For example, if the monitor_action is set to freezestop, the script can decide to crash the server if it detects a situation were the freezestop can not succeed (ex. fs can not be umounted with a dead storage array).",
                  example="/bin/true"
                )

class KeywordCreatePg(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="DEFAULT",
                  keyword="create_pg",
                  order=30,
                  default=True,
                  convert="boolean",
                  candidates=(True, False),
                  text="Use process containers when possible. Containers allow capping memory, swap and cpu usage per service. Lxc containers are naturally containerized, so skip containerization of their startapp."
                )

class KeywordAppScript(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="app",
                  keyword="script",
                  at=True,
                  order=9,
                  required=False,
                  text="Full path to the app launcher script. Or its basename if the file is hosted in the <svcname>.d path. This script must accept as arg0 the activated actions word: start for start, stop for stop, status for check, info for info."
                )

class KeywordAppTimeout(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="app",
                  keyword="timeout",
                  convert="duration",
                  order=9,
                  at=True,
                  text="Wait for <duration> before declaring the app launcher action a failure. Can be overriden by :kw:`<action>_timeout`. If no timeout is set, the agent waits indefinitely for the app launcher to return. A timeout can be coupled with :kw:`optional=true` to not abort a servicea instance start when an app launcher did not return.",
                  example="180"
                )

class KeywordAppStartTimeout(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="app",
                  keyword="start_timeout",
                  convert="duration",
                  order=9,
                  at=True,
                  text="Wait for <duration> before declaring the app launcher start action a failure. Takes precedence over :kw:`timeout`. If neither :kw:`timeout` nor :kw:`start_timeout` is set, the agent waits indefinitely for the app launcher to return. A timeout can be coupled with :kw:`optional=true` to not abort a servicea instance start when an app launcher did not return.",
                  example="180"
                )

class KeywordAppStopTimeout(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="app",
                  keyword="stop_timeout",
                  convert="duration",
                  order=9,
                  at=True,
                  text="Wait for <duration> before declaring the app launcher stop action a failure. Takes precedence over :kw:`timeout`. If neither :kw:`timeout` nor :kw:`stop_timeout` is set, the agent waits indefinitely for the app launcher to return. A timeout can be coupled with :kw:`optional=true` to not abort a servicea instance stop when an app launcher did not return.",
                  example="180"
                )

class KeywordAppCheckTimeout(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="app",
                  keyword="check_timeout",
                  convert="duration",
                  order=9,
                  at=True,
                  text="Wait for <duration> before declaring the app launcher check action a failure. Takes precedence over :kw:`timeout`. If neither :kw:`timeout` nor :kw:`check_timeout` is set, the agent waits indefinitely for the app launcher to return. A timeout can be coupled with :kw:`optional=true` to not abort a servicea instance check when an app launcher did not return.",
                  example="180"
                )

class KeywordAppInfoTimeout(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="app",
                  keyword="info_timeout",
                  convert="duration",
                  order=9,
                  at=True,
                  text="Wait for <duration> before declaring the app launcher info action a failure. Takes precedence over :kw:`timeout`. If neither :kw:`timeout` nor :kw:`info_timeout` is set, the agent waits indefinitely for the app launcher to return. A timeout can be coupled with :kw:`optional=true` to not abort a servicea instance info when an app launcher did not return.",
                  example="180"
                )

class KeywordAppStart(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="app",
                  keyword="start",
                  at=True,
                  order=10,
                  default=False,
                  text="true: execute '<script> start' on start action. false: do nothing on start action. <shlex expression>: execute the command on start.",
                )

class KeywordAppStop(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="app",
                  keyword="stop",
                  at=True,
                  order=11,
                  default=False,
                  text="true: execute '<script> stop' on stop action. false: do nothing on stop action. <shlex expression>: execute the command on stop action.",
                )

class KeywordAppCheck(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="app",
                  keyword="check",
                  at=True,
                  order=11,
                  default=False,
                  text="true: execute '<script> status' on status evaluation. false: do nothing on status evaluation. <shlex expression>: execute the command on status evaluation.",
                )

class KeywordAppInfo(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="app",
                  keyword="info",
                  at=True,
                  order=12,
                  default=False,
                  text="true: execute '<script> info' on info action. false: do nothing on info action. <shlex expression>: execute the command on info action.",
                )

class KeywordAppLimitAs(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="app",
                  keyword="limit_as",
                  convert="size",
                  at=True,
                  order=12,
                  text=""
                )
class KeywordAppLimitCpu(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="app",
                  keyword="limit_cpu",
                  convert="duration",
                  at=True,
                  order=12,
                  text=""
                )
class KeywordAppLimitCore(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="app",
                  keyword="limit_core",
                  convert="size",
                  at=True,
                  order=12,
                  text=""
                )
class KeywordAppLimitData(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="app",
                  keyword="limit_data",
                  convert="size",
                  at=True,
                  order=12,
                  text=""
                )
class KeywordAppLimitFsize(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="app",
                  keyword="limit_fsize",
                  convert="size",
                  at=True,
                  order=12,
                  text=""
                )
class KeywordAppLimitMemlock(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="app",
                  keyword="limit_memlock",
                  convert="size",
                  at=True,
                  order=12,
                  text=""
                )
class KeywordAppLimitNofile(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="app",
                  keyword="limit_nofile",
                  convert="size",
                  at=True,
                  order=12,
                  text=""
                )
class KeywordAppLimitNproc(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="app",
                  keyword="limit_nproc",
                  convert="size",
                  at=True,
                  order=12,
                  text=""
                )
class KeywordAppLimitRss(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="app",
                  keyword="limit_rss",
                  convert="size",
                  at=True,
                  order=12,
                  text=""
                )
class KeywordAppLimitStack(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="app",
                  keyword="limit_stack",
                  convert="size",
                  at=True,
                  order=12,
                  text=""
                )
class KeywordAppLimitVmem(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="app",
                  keyword="limit_vmem",
                  convert="size",
                  at=True,
                  order=12,
                  text=""
                )

class KeywordSyncType(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="type",
                  order=10,
                  candidates=("rsync", "docker", "dds", "netapp", "symsrdfs", "zfs", "btrfs", "symclone", "symsnap", "hp3par", "hp3parsnap", "evasnap", "ibmdssnap", "dcssnap", "dcsckpt", "necismsnap", "zfssnap", "btrfssnap", "rados", "s3"),
                  default="rsync",
                  text="Point a sync driver to use."
                )

class KeywordSyncDockerTarget(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="target",
                  convert="list",
                  rtype="docker",
                  order=11,
                  at=True,
                  required=True,
                  candidates=["nodes", "drpnodes"],
                  text="Destination nodes of the sync."
                )

class KeywordSyncS3Snar(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="snar",
                  rtype="s3",
                  order=10,
                  at=True,
                  example="/srv/mysvc/var/sync.1.snar",
                  text="The GNU tar snar file full path. The snar file stored the GNU tar metadata needed to do an incremental tarball. If the service fails over shared disks the snar file should be stored there, so the failover node can continue the incremental cycle."
                )

class KeywordSyncS3Src(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="src",
                  convert="list",
                  rtype="s3",
                  order=10,
                  at=True,
                  required=True,
                  example="/srv/mysvc/tools /srv/mysvc/apps*",
                  text="Source globs as passed as paths to archive to a tar command."
                )

class KeywordSyncS3Options(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="options",
                  convert="list",
                  rtype="s3",
                  order=10,
                  at=True,
                  example="--exclude *.pyc",
                  text="Options passed to GNU tar for archiving."
                )

class KeywordSyncS3Bucket(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="bucket",
                  rtype="s3",
                  order=10,
                  at=True,
                  required=True,
                  example="opensvc-myapp",
                  text="The name of the S3 bucket to upload the backup to."
                )

class KeywordSyncS3FullSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="full_schedule",
                  rtype="s3",
                  order=10,
                  at=True,
                  example="@1441 sun",
                  text="The schedule of full backups. sync_update actions are triggered according to the resource 'schedule' parameter, and do a full backup if the current date matches the 'full_schedule' parameter or an incremental backup otherwise."
                )

class KeywordSyncZfsSnapRecursive(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="recursive",
                  rtype="zfssnap",
                  order=10,
                  at=True,
                  example="true",
                  default=True,
                  convert="boolean",
                  text="Set to true to snap recursively the datasets."
                )

class KeywordSyncZfsSnapName(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="name",
                  rtype="zfssnap",
                  order=10,
                  at=True,
                  example="weekly",
                  text="A name included in the snapshot name to avoid retention conflicts between multiple zfs snapshot resources. A full snapshot name is formatted as <subvol>.<name>.snap.<datetime>. Example: data.weekly.snap.2016-03-09.10:09:52"
                )

class KeywordSyncZfsSnapDataset(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="dataset",
                  convert="list",
                  rtype="zfssnap",
                  order=10,
                  at=True,
                  required=True,
                  example="svc1fs/data svc1fs/log",
                  text="A whitespace separated list of datasets to snapshot."
                )

class KeywordSyncZfsSnapKeep(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="keep",
                  rtype="zfssnap",
                  order=10,
                  at=True,
                  default=3,
                  convert="integer",
                  example="3",
                  text="The maximum number of snapshots to retain."
                )

class KeywordSyncBtrfsSnapName(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="name",
                  rtype="btrfssnap",
                  order=10,
                  at=True,
                  example="weekly",
                  text="A name included in the snapshot name to avoid retention conflicts between multiple btrfs snapshot resources. A full snapshot name is formatted as <subvol>.<name>.snap.<datetime>. Example: data.weekly.snap.2016-03-09.10:09:52"
                )

class KeywordSyncBtrfsSnapSubvol(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="subvol",
                  convert="list",
                  rtype="btrfssnap",
                  order=10,
                  at=True,
                  required=True,
                  example="svc1fs:data svc1fs:log",
                  text="A whitespace separated list of <label>:<subvol> to snapshot."
                )

class KeywordSyncBtrfsSnapKeep(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="keep",
                  rtype="btrfssnap",
                  order=10,
                  at=True,
                  default=3,
                  convert="integer",
                  example="3",
                  text="The maximum number of snapshots to retain."
                )

class KeywordSyncBtrfsSrc(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="src",
                  convert="list",
                  rtype="btrfs",
                  order=10,
                  at=True,
                  required=True,
                  text="Source subvolume of the sync."
                )

class KeywordSyncBtrfsDst(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="dst",
                  rtype="btrfs",
                  order=10,
                  at=True,
                  required=True,
                  text="Destination subvolume of the sync."
                )

class KeywordSyncBtrfsTarget(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="target",
                  convert="list",
                  rtype="btrfs",
                  order=11,
                  at=True,
                  required=True,
                  candidates=["nodes", "drpnodes"],
                  text="Destination nodes of the sync."
                )

class KeywordSyncBtrfsRecursive(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="recursive",
                  rtype="btrfs",
                  order=10,
                  at=True,
                  default=False,
                  convert="boolean",
                  candidates=[True, False],
                  text="Also replicate subvolumes in the src tree."
                )

class KeywordSyncZfsSrc(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="src",
                  rtype="zfs",
                  order=10,
                  at=True,
                  required=True,
                  text="Source dataset of the sync."
                )

class KeywordSyncZfsDst(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="dst",
                  rtype="zfs",
                  order=11,
                  at=True,
                  required=True,
                  text="Destination dataset of the sync."
                )

class KeywordSyncZfsTarget(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="target",
                  convert="list",
                  rtype="zfs",
                  order=12,
                  required=True,
                  candidates=['nodes', 'drpnodes'],
                  text="Describes which nodes should receive this data sync from the PRD node where the service is up and running. SAN storage shared 'nodes' must not be sync to 'nodes'. SRDF-like paired storage must not be sync to 'drpnodes'."
                )

class KeywordSyncZfsRecursive(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="recursive",
                  rtype="zfs",
                  at=True,
                  order=13,
                  default=True,
                  convert="boolean",
                  candidates=(True, False),
                  text="Describes which nodes should receive this data sync from the PRD node where the service is up and running. SAN storage shared 'nodes' must not be sync to 'nodes'. SRDF-like paired storage must not be sync to 'drpnodes'."
                )

class KeywordSyncZfsTags(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="tags",
                  rtype="zfs",
                  convert="set",
                  default=set(),
                  at=True,
                  text="The zfs sync resource supports the 'delay_snap' tag. This tag is used to delay the snapshot creation just before the sync, thus after 'postsnap_trigger' execution. The default behaviour (no tags) is to group all snapshots creation before copying data to remote nodes, thus between 'presnap_trigger' and 'postsnap_trigger'."
                )

class KeywordSyncRsyncSrc(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="src",
                  convert="list",
                  rtype="rsync",
                  order=10,
                  at=True,
                  required=True,
                  text="Source of the sync. Can be a whitespace-separated list of files or dirs passed as-is to rsync. Beware of the meaningful ending '/'. Refer to the rsync man page for details."
                )

class KeywordSyncRsyncDst(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="dst",
                  rtype="rsync",
                  order=11,
                  required=True,
                  text="Destination of the sync. Beware of the meaningful ending '/'. Refer to the rsync man page for details."
                )

class KeywordSyncRsyncTags(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="tags",
                  convert="set",
                  default=set(),
                  default_text="",
                  example="foo bar",
                  at=True,
                  rtype="rsync",
                  text="The sync resource supports the 'delay_snap' tag. This tag is used to delay the snapshot creation just before the rsync, thus after 'postsnap_trigger' execution. The default behaviour (no tags) is to group all snapshots creation before copying data to remote nodes, thus between 'presnap_trigger' and 'postsnap_trigger'."
                )

class KeywordSyncRsyncOptions(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="options",
                  convert="shlex",
                  default=[],
                  default_text="",
                  example="--acls --xattrs --exclude foo/bar",
                  at=True,
                  rtype="rsync",
                  text="A whitespace-separated list of params passed unchanged to rsync. Typical usage is ACL preservation activation."
                )

class KeywordSyncRsyncTarget(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="target",
                  convert="list",
                  rtype="rsync",
                  order=12,
                  required=True,
                  candidates=['nodes', 'drpnodes'],
                  text="Describes which nodes should receive this data sync from the PRD node where the service is up and running. SAN storage shared 'nodes' must not be sync to 'nodes'. SRDF-like paired storage must not be sync to 'drpnodes'."
                )

class KeywordSyncRsyncSnap(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="snap",
                  rtype="rsync",
                  at=True,
                  order=14,
                  candidates=(True, False),
                  default=False,
                  convert="boolean",
                  text="If set to true, OpenSVC will try to snapshot the first snapshottable parent of the source of the sync and try to sync from the snap."
                )

class KeywordSyncRsyncDstfs(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="dstfs",
                  rtype="rsync",
                  order=13,
                  text="If set to a remote mount point, OpenSVC will verify that the specified mount point is really hosting a mounted FS. This can be used as a safety net to not overflow the parent FS (may be root)."
                )

class KeywordSyncRsyncBwlimit(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="bwlimit",
                  convert="integer",
                  rtype="rsync",
                  text="Bandwidth limit in KB applied to this rsync transfer. Leave empty to enforce no limit. Takes precedence over 'bwlimit' set in [DEFAULT]."
                )

class KeywordSyncSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="schedule",
                  at=True,
                  text="Set the this resource synchronization schedule. See usr/share/doc/node.conf for the schedule syntax reference.",
                  example='["00:00-01:00@61 mon", "02:00-03:00@61 tue-sun"]'
                )

class KeywordSyncSyncMaxDelay(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="sync_max_delay",
                  default=1440,
                  convert="duration",
                  text="Unit is minutes. This sets to delay above which the sync status of the resource is to be considered down. Should be set according to your application service level agreement. The scheduler task frequency should be set between 'sync_min_delay' and 'sync_max_delay'."
                )

class KeywordIpIpname(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="ip",
                  keyword="ipname",
                  order=12,
                  required=False,
                  at=True,
                  text="The DNS name or IP address of the ip resource. Can be different from one node to the other, in which case '@nodename' can be specified. This is most useful to specify a different ip when the service starts in DRP mode, where subnets are likely to be different than those of the production datacenter. With the amazon driver, the special <allocate> value tells the provisioner to assign a new private address."
                )

class KeywordIpDnsNameSuffix(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="ip",
                  keyword="dns_name_suffix",
                  order=12,
                  at=True,
                  text="Add the value as a suffix to the DNS record name. The record created is thus formatted as <svcname>-<dns_name_suffix>.<app>.<managed zone>."
                )

class KeywordIpProvisioner(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="ip",
                  keyword="provisioner",
                  provisioning=True,
                  candidates=("collector", None),
                  order=12,
                  at=True,
                  example="collector",
                  text="The IPAM driver to use to provision the ip.",
                )

class KeywordIpNetwork(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="ip",
                  keyword="network",
                  order=12,
                  at=True,
                  example="10.0.0.0",
                  text="The network, in dotted notation, from where the ip provisioner allocates. Also used by the docker ip driver to delete the network route if del_net_route is set to true.",
                )

class KeywordIpDnsUpdate(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="ip",
                  keyword="dns_update",
                  order=12,
                  at=True,
                  default=False,
                  convert="boolean",
                  candidates=[True, False],
                  text="Setting this parameter triggers a DNS update. The record created is formatted as <svcname>.<app>.<managed zone>, unless dns_record_name is specified."
                )

class KeywordIpZone(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="ip",
                  keyword="zone",
                  order=12,
                  at=True,
                  text="The zone name the ip resource is linked to. If set, the ip is plumbed from the global in the zone context.",
                  example="zone1"
                )

class KeywordIpDockerContainerRid(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="ip",
                  rtype=["cni", "docker"],
                  keyword="container_rid",
                  order=12,
                  at=True,
                  required=True,
                  text="The resource id of the container to plumb the ip into.",
                  example="container#0"
                )

class KeywordIpCniNetwork(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="ip",
                  rtype="cni",
                  keyword="network",
                  order=12,
                  at=True,
                  required=True,
                  text="The name of the CNI network to plug into.",
                  example="container#0"
                )

class KeywordIpExpose(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="ip",
                  keyword="expose",
                  order=12,
                  at=True,
                  required=False,
                  default=[],
                  convert="list",
                  text="A whitespace-separated list of <port>/<protocol>[:<host port>] "
                       "describing socket services that mandate a SRV exposition. With "
                       "<host_port> set, the ip.cni driver configures port mappings too.",
                  example="443/tcp:8443 53/udp"
                )

class KeywordIpCniContainerRid(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="ip",
                  rtype="cni",
                  keyword="container_rid",
                  order=12,
                  at=True,
                  required=False,
                  text="The resource id of the container to plug into the CNI network.",
                  example="container#0"
                )

class KeywordIpAmazonEip(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="ip",
                  rtype="amazon",
                  keyword="eip",
                  order=12,
                  at=True,
                  text="The public elastic ip to associate to <ipname>. The special <allocate> value tells the provisioner to assign a new public address.",
                  example="52.27.90.63"
                )

class KeywordIpAmazonCascadeAllocation(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="ip",
                  rtype="amazon",
                  keyword="cascade_allocation",
                  provisioning=True,
                  order=13,
                  at=True,
                  text="Set new allocated ip as value to other ip resources ipname parameter. The syntax is a whitespace separated list of <rid>.ipname[@<scope>].",
                  example="ip#1.ipname ip#1.ipname@nodes"
                )

class KeywordIpAmazonDockerDaemonIp(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="ip",
                  rtype="amazon",
                  keyword="docker_daemon_ip",
                  provisioning=True,
                  order=13,
                  at=False,
                  candidates=[True, False],
                  text="Set new allocated ip as value as a '--ip <addr>' argument in the DEFAULT.docker_daemon_args parameter.",
                  example="True"
                )

class KeywordDiskPrKey(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  keyword="prkey",
                  order=15,
                  at=True,
                  text="Defines a specific persistent reservation key for the resource. Takes priority over the service-level defined prkey and the node.conf specified prkey."
                )

class KeywordDiskGceNames(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="gce",
                  keyword="names",
                  convert="list",
                  order=1,
                  at=True,
                  required=True,
                  text="Set the gce disk names",
                  example="svc1-disk1"
                )

class KeywordDiskGceZone(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="gce",
                  keyword="gce_zone",
                  order=2,
                  at=True,
                  required=True,
                  text="Set the gce zone",
                  example="europe-west1-b"
                )

class KeywordDiskGceDescription(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="gce",
                  keyword="description",
                  provisioning=True,
                  order=5,
                  at=True,
                  text="An optional, textual description for the disks being created.",
                  example="foo"
                )

class KeywordDiskGceImage(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="gce",
                  keyword="image",
                  provisioning=True,
                  order=5,
                  at=True,
                  text="An image to apply to the disks being created. When using this option, the size of the disks must be at least as large as the image size.",
                  example="centos-7"
                )

class KeywordDiskGceImageProject(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="gce",
                  keyword="image_project",
                  provisioning=True,
                  order=5,
                  at=True,
                  text="The project against which all image references will be resolved.",
                  example="myprj"
                )

class KeywordDiskGceSize(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="gce",
                  keyword="size",
                  provisioning=True,
                  order=3,
                  at=True,
                  convert="size",
                  text="Indicates the size of the disks. The OpenSVC size converter is used to produce gce compatible size, so k, K, kib, KiB, kb, KB, ki, Ki and all their g, t, p, e variants are supported.",
                  example="20g"
                )

class KeywordDiskGceSourceSnapshot(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="gce",
                  keyword="source_snapshot",
                  provisioning=True,
                  order=5,
                  at=True,
                  text="A source snapshot used to create the disks. It is safe to delete a snapshot after a disk has been created from the snapshot. In such cases, the disks will no longer reference the deleted snapshot. When using this option, the size of the disks must be at least as large as the snapshot size.",
                  example="mysrcsnap"
                )


class KeywordDiskGceDiskType(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="gce",
                  keyword="disk_type",
                  provisioning=True,
                  order=5,
                  at=True,
                  text="Specifies the type of disk to create. To get a list of available disk types, run 'gcloud compute disk-types list'. The default disk type is pd-standard.",
                  example="pd-standard"
                )

class KeywordIpGceRoutename(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="ip",
                  rtype="gce",
                  keyword="routename",
                  order=13,
                  at=True,
                  text="Set the gce route name",
                  example="rt-ip1"
                )

class KeywordIpGceZone(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="ip",
                  rtype="gce",
                  keyword="gce_zone",
                  order=13,
                  at=True,
                  text="Set the gce ip route next hop zone",
                  example="europe-west1-b"
                )

class KeywordIpType(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="ip",
                  keyword="type",
                  at=True,
                  candidates=[None, 'crossbow', 'amazon', 'docker', 'gce', 'cni'],
                  text="The opensvc ip driver name.",
                  order=10,
                  example="crossbow",
                )

class KeywordIpIpdev(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="ip",
                  keyword="ipdev",
                  order=11,
                  at=True,
                  required=True,
                  text="The interface name over which OpenSVC will try to stack the service ip. Can be different from one node to the other, in which case the '@nodename' can be specified."
                )

class KeywordIpIpdevext(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="ip",
                  rtype="crossbow",
                  keyword="ipdevext",
                  order=12,
                  at=True,
                  example="v4",
                  default="v4",
                  text="The interface name extension for crossbow ipadm configuration."
                )

class KeywordIpDelNetRoute(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="ip",
                  rtype="docker",
                  keyword="del_net_route",
                  order=12,
                  at=True,
                  default=False,
                  example="true",
                  text="Some docker ip configuration requires dropping the network route autoconfigured when installing the ip address. In this case set this parameter to true, and also set the network parameter."
                )

class KeywordIpNetmask(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="ip",
                  keyword="netmask",
                  at=True,
                  order=13,
                  text="If an ip is already plumbed on the root interface (in which case the netmask is deduced from this ip). Mandatory if the interface is dedicated to the service (dummy interface are likely to be in this case). The format is either dotted or octal for IPv4, ex: 255.255.252.0 or 22, and octal for IPv6, ex: 64.",
                  example="255.255.255.0"
                )

class KeywordIpGateway(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="ip",
                  keyword="gateway",
                  at=True,
                  order=14,
                  text="A zone ip provisioning parameter used in the sysidcfg formatting. The format is decimal for IPv4, ex: 255.255.252.0, and octal for IPv6, ex: 64.",
                  provisioning=True
                )

class KeywordDiskType(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  keyword="type",
                  at=True,
                  order=9,
                  default="vg",
                  candidates=["disk", "veritas", "raw", "rados", "md", "drbd", "loop", "zpool", "pool", "raw", "vmdg", "vdisk", "lvm", "vg", "lv", "amazon", "gce"],
                  text="The volume group driver to use. Leave empty to activate the native volume group manager."
                )

class KeywordFsDockerDriver(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="fs",
                  rtype="docker",
                  keyword="driver",
                  default="local",
                  order=10,
                  at=True,
                  text="The docker volume driver to use for the resource.",
                  example="tmpfs"
                )

class KeywordFsDockerOptions(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="fs",
                  rtype="docker",
                  keyword="options",
                  order=10,
                  at=True,
                  convert="shlex",
                  text="The docker volume create options to use for the resource. --label and --opt",
                  example="--opt o=size=100m,uid=1000 --opt type=tmpfs --opt device=tmpfs"
                )

class KeywordFsPopulate(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="fs",
                  keyword="populate",
                  order=10,
                  at=True,
                  convert="list",
                  provisioning=True,
                  text="The list of rulesets providing files to install in the volume.",
                  example="configmap.redis configmap.global"
                )

class KeywordDiskLvName(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="lv",
                  keyword="name",
                  required=True,
                  order=10,
                  at=True,
                  text="The name of the logical volume.",
                  example="lv1"
                )

class KeywordDiskLvVg(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="lv",
                  keyword="vg",
                  order=11,
                  at=True,
                  text="The name of the volume group hosting the logical volume.",
                  example="vg1"
                )

class KeywordDiskLvSize(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="lv",
                  keyword="size",
                  convert="size",
                  order=12,
                  at=True,
                  provisioning=True,
                  text="The size of the logical volume to provision. A size expression or <n>%{FREE|PVS|VG}.",
                  example="10m"
                )

class KeywordDiskLvCreateOptions(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="lv",
                  keyword="create_options",
                  convert="shlex",
                  order=13,
                  at=True,
                  provisioning=True,
                  text="Additional options to pass to the logical volume create command. Size and name are alread set.",
                  example="--contiguous y"
                )

class KeywordDiskFsCreateOptions(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="fs",
                  rtype=rcEnv.fs_non_pooling,
                  keyword="create_options",
                  convert="shlex",
                  order=13,
                  at=True,
                  provisioning=True,
                  text="Additional options to pass to the logical volume create command. Size and name are alread set.",
                  example="--contiguous y"
                )

class KeywordDiskDiskDiskId(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="disk",
                  keyword="disk_id",
                  order=10,
                  at=True,
                  text="The wwn of the disk.",
                  example="6589cfc00000097484f0728d8b2118a6"
                )

class KeywordDiskDiskSize(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="disk",
                  keyword="size",
                  convert="size",
                  order=11,
                  at=True,
                  provisioning=True,
                  text="The size of the disk to provision.",
                  example="15g"
                )

class KeywordDiskDiskArray(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="disk",
                  keyword="array",
                  order=11,
                  at=True,
                  provisioning=True,
                  text="The array to provision the disk from.",
                  example="xtremio-prod1"
                )

class KeywordDiskDiskDiskGroup(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="disk",
                  keyword="diskgroup",
                  order=11,
                  at=True,
                  provisioning=True,
                  text="The array disk group to provision the disk from.",
                  example="default"
                )

class KeywordDiskDiskSlo(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="disk",
                  keyword="slo",
                  order=11,
                  at=True,
                  provisioning=True,
                  text="The provisioned disk service level objective. This keyword is honored on arrays supporting this (ex: EMC VMAX)",
                  example="Optimized"
                )

class KeywordDiskAmazonVolumes(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="amazon",
                  keyword="volumes",
                  convert="list",
                  order=10,
                  at=True,
                  required=True,
                  text="A whitespace separated list of amazon volumes. Any member of the list can be set to a special <key=value,key=value> value. In this case the provisioner will allocate a new volume with the specified characteristics and replace this member with the allocated volume id. The supported keys are the same as those supported by the awscli ec2 create-volume command: size, iops, availability-zone, snapshot, type and encrypted.",
                  example="vol-123456 vol-654321"
                )

class KeywordDiskRawDevs(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="raw",
                  keyword="devs",
                  convert="set",
                  order=10,
                  at=True,
                  required=True,
                  text="a list of device paths or <src>:<dst> device paths mappings, whitespace separated. Those devices are owned by the service and scsi reservation policy is applied to them.",
                  example="/dev/mapper/svc.d0:/dev/oracle/redo001 /dev/mapper/svc.d1"
                )

class KeywordDiskRawZone(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="raw",
                  keyword="zone",
                  order=12,
                  at=True,
                  text="The zone name the raw resource is linked to. If set, the raw files are configured from the global reparented to the zonepath.",
                  example="zone1"
                )

class KeywordDiskRawCreateCharDevices(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="raw",
                  keyword="create_char_devices",
                  order=10,
                  at=True,
                  default=False,
                  convert="boolean",
                  text="On Linux, char devices are not automatically created when devices are discovered. If set to True (the default), the raw resource driver will create and delete them using the raw kernel driver.",
                  example="false"
                )

class KeywordDiskRawUser(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  keyword="user",
                  rtype="raw",
                  order=11,
                  at=True,
                  example="root",
                  text="The user that should be owner of the device. Either in numeric or symbolic form."
                )

class KeywordDiskRawGroup(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  keyword="group",
                  rtype="raw",
                  order=11,
                  at=True,
                  example="sys",
                  text="The group that should be owner of the device. Either in numeric or symbolic form."
                )

class KeywordDiskRawPerm(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  keyword="perm",
                  rtype="raw",
                  order=11,
                  at=True,
                  example="600",
                  text="The permissions the device should have. A string representing the octal permissions."
                )


class KeywordDiskVgName(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype=["lvm", "vg", "veritas"],
                  keyword="name",
                  order=10,
                  at=True,
                  required=True,
                  text="The name of the volume group"
                )

class KeywordDiskOptions(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype=["lvm", "vg"],
                  keyword="options",
                  default="",
                  at=True,
                  provisioning=True,
                  text="The vgcreate options to use upon vg provisioning."
                )

class KeywordDiskMdUuid(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  required=False,
                  at=True,
                  keyword="uuid",
                  rtype="md",
                  text="The md uuid to use with mdadm assemble commands"
                )

class KeywordDiskMdDevs(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  required=True,
                  at=True,
                  keyword="devs",
                  rtype="md",
                  provisioning=True,
                  example="/dev/rbd0 /dev/rbd1",
                  text="The md member devices to use with mdadm create command"
                )

class KeywordDiskMdLevel(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  required=True,
                  at=True,
                  keyword="level",
                  rtype="md",
                  provisioning=True,
                  example="raid1",
                  text="The md raid level to use with mdadm create command (see mdadm man for values)"
                )

class KeywordDiskMdLayout(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  at=True,
                  keyword="layout",
                  rtype="md",
                  provisioning=True,
                  text="The md raid layout to use with mdadm create command (see mdadm man for values)"
                )

class KeywordDiskMdChunk(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  at=True,
                  keyword="chunk",
                  rtype="md",
                  provisioning=True,
                  example="128k",
                  text="The md chunk size to use with mdadm create command. Values are converted to kb and rounded to 4."
                )

class KeywordDiskMdSpares(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  at=True,
                  keyword="spares",
                  rtype="md",
                  provisioning=True,
                  convert="integer",
                  example="0",
                  text="The md number of spare devices to use with mdadm create command"
                )

class KeywordSyncRadosPairs(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="pairs",
                  convert="list",
                  rtype="radosclone",
                  required=True,
                  at=True,
                  text="The rados clone device pairs."
                )

class KeywordDiskClientId(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  keyword="client_id",
                  rtype="rados",
                  text="Client id to use for authentication with the rados servers."
                )

class KeywordDiskKeyring(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  keyword="keyring",
                  rtype="rados",
                  text="keyring to look for the client id secret for authentication with the rados servers."
                )

class KeywordDiskLock(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  keyword="lock",
                  candidates=["exclusive", "shared", "None"],
                  rtype="rados",
                  text="Locking mode for the rados images"
                )

class KeywordDiskLockSharedTag(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  keyword="lock_shared_tag",
                  rtype="rados",
                  depends=[('lock', ['shared'])],
                  text="The tag to use upon rados image locking in shared mode"
                )

class KeywordDiskImageFormat(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="rados",
                  keyword="image_format",
                  provisioning=True,
                  default="2",
                  text="The rados image format"
                )

class KeywordDiskSize(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="rados",
                  keyword="size",
                  convert="size",
                  provisioning=True,
                  text="The rados image size in MB"
                )

class KeywordDiskImages(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="rados",
                  keyword="images",
                  convert="list",
                  required=True,
                  text="The rados image names handled by this vg resource. whitespace separated."
                )

class KeywordSyncRadosImages(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="radossnap",
                  keyword="images",
                  convert="list",
                  required=True,
                  text="The rados image names handled by this sync resource. whitespace separated."
                )

class KeywordDiskDsf(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype=["lvm", "vg"],
                  keyword="dsf",
                  candidates=(True, False),
                  default=True,
                  convert="boolean",
                  text="HP-UX only. 'dsf' must be set to false for LVM to use never-multipathed /dev/dsk/... devices. Otherwize, ad-hoc multipathed /dev/disk/... devices."
                )

class KeywordDiskScsireserv(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  keyword="scsireserv",
                  default=False,
                  convert="boolean",
                  candidates=(True, False),
                  text="If set to 'true', OpenSVC will try to acquire a type-5 (write exclusive, registrant only) scsi3 persistent reservation on every path to every disks held by this resource. Existing reservations are preempted to not block service start-up. If the start-up was not legitimate the data are still protected from being written over from both nodes. If set to 'false' or not set, 'scsireserv' can be activated on a per-resource basis."
                )

class KeywordDiskPromoteRW(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  keyword="promote_rw",
                  default=False,
                  convert="boolean",
                  candidates=(True, False),
                  text="If set to 'true', OpenSVC will try to promote the base devices to read-write on start."
                )

class KeywordDiskNoPreemptAbort(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  keyword="no_preempt_abort",
                  order=15,
                  at=True,
                  candidates=(True, False),
                  default=False,
                  convert="boolean",
                  text="If set to 'true', OpenSVC will preempt scsi reservation with a preempt command instead of a preempt and and abort. Some scsi target implementations do not support this last mode (esx). If set to 'false' or not set, 'no_preempt_abort' can be activated on a per-resource basis."
                )

class KeywordFsPromoteRW(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="fs",
                  keyword="promote_rw",
                  default=False,
                  convert="boolean",
                  candidates=(True, False),
                  text="If set to 'true', OpenSVC will try to promote the base devices to read-write on start."
                )

class KeywordFsScsireserv(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="fs",
                  keyword="scsireserv",
                  default=False,
                  convert="boolean",
                  candidates=(True, False),
                  text="If set to 'true', OpenSVC will try to acquire a type-5 (write exclusive, registrant only) scsi3 persistent reservation on every path to every disks held by this resource. Existing reservations are preempted to not block service start-up. If the start-up was not legitimate the data are still protected from being written over from both nodes. If set to 'false' or not set, 'scsireserv' can be activated on a per-resource basis."
                )

class KeywordFsNoPreemptAbort(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="fs",
                  keyword="no_preempt_abort",
                  order=15,
                  at=True,
                  candidates=(True, False),
                  default=False,
                  convert="boolean",
                  text="If set to 'true', OpenSVC will preempt scsi reservation with a preempt command instead of a preempt and and abort. Some scsi target implementations do not support this last mode (esx). If set to 'false' or not set, 'no_preempt_abort' can be activated on a per-resource basis."
                )

class KeywordContainerPromoteRW(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="promote_rw",
                  default=False,
                  convert="boolean",
                  candidates=(True, False),
                  text="If set to 'true', OpenSVC will try to promote the base devices to read-write on start."
                )

class KeywordContainerScsireserv(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="scsireserv",
                  default=False,
                  convert="boolean",
                  candidates=(True, False),
                  text="If set to 'true', OpenSVC will try to acquire a type-5 (write exclusive, registrant only) scsi3 persistent reservation on every path to every disks held by this resource. Existing reservations are preempted to not block service start-up. If the start-up was not legitimate the data are still protected from being written over from both nodes. If set to 'false' or not set, 'scsireserv' can be activated on a per-resource basis."
                )

class KeywordContainerNoPreemptAbort(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="container",
                  keyword="no_preempt_abort",
                  order=15,
                  at=True,
                  candidates=(True, False),
                  default=False,
                  convert="boolean",
                  text="If set to 'true', OpenSVC will preempt scsi reservation with a preempt command instead of a preempt and and abort. Some scsi target implementations do not support this last mode (esx). If set to 'false' or not set, 'no_preempt_abort' can be activated on a per-resource basis."
                )

class KeywordDiskPvs(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype=["lvm", "vg"],
                  keyword="pvs",
                  required=True,
                  text="The list of paths to the physical volumes of the volume group.",
                  provisioning=True
                )

class KeywordZPoolVdev(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype=["zpool", "pool"],
                  keyword="vdev",
                  required=True,
                  convert="list",
                  order=11,
                  at=True,
                  provisioning=True,
                  text="The vdev list, including optional parity keywords, as would be passed to zpool create."
                )

class KeywordZPoolName(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype=["zpool", "pool"],
                  keyword="name",
                  order=10,
                  at=True,
                  text="The name of the zfs pool"
                )

class KeywordZPoolZone(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  rtype="zpool",
                  section="disk",
                  keyword="zone",
                  order=11,
                  at=True,
                  text="The zone name the zpool refers to. If set, the zpool is activated in the zone context."
                )

class KeywordVmdgContainerid(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="vmdg",
                  keyword="container_id",
                  required=True,
                  at=True,
                  text="The id of the container whose configuration to extract the disk mapping from."
                )

class KeywordDiskDrbdRes(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="drbd",
                  keyword="res",
                  order=11,
                  required=True,
                  text="The name of the drbd resource associated with this service resource. OpenSVC expect the resource configuration file to reside in '/etc/drbd.d/resname.res'. The 'sync#i0' resource will take care of replicating this file to remote nodes."
                )

class KeywordShareType(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="share",
                  keyword="type",
                  candidates=["nfs"],
                  text="The type of share.",
                  required=True,
                  order=1
                )

class KeywordSharePath(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="share",
                  keyword="path",
                  rtype="nfs",
                  order=10,
                  at=True,
                  required=True,
                  text="The fullpath of the directory to share."
                )

class KeywordShareNfsOpts(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="share",
                  keyword="opts",
                  rtype="nfs",
                  order=11,
                  at=True,
                  required=True,
                  text="The NFS share export options, as they woud be set in /etc/exports or passed to Solaris share command."
                )

class KeywordFsDev(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="fs",
                  keyword="dev",
                  order=11,
                  at=True,
                  required=True,
                  text="The block device file or filesystem image file hosting the filesystem to mount. Different device can be set up on different nodes using the dev@nodename syntax"
                )

class KeywordFsZone(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="fs",
                  keyword="zone",
                  order=11,
                  at=True,
                  text="The zone name the fs refers to. If set, the fs mount point is reparented into the zonepath rootfs."
                )

class KeywordFsVg(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="fs",
                  keyword="vg",
                  rtype=rcEnv.fs_non_pooling,
                  required=False,
                  at=True,
                  text="The name of the disk group the filesystem device should be provisioned from.",
                  provisioning=True
                )

class KeywordFsSize(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="fs",
                  rtype=rcEnv.fs_non_pooling,
                  keyword="size",
                  required=True,
                  convert="size",
                  at=True,
                  text="The size of the logical volume to provision for this filesystem. A size expression or <n>%{FREE|PVS|VG}.",
                  provisioning=True
                )

class KeywordFsZfsSize(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="fs",
                  rtype="zfs",
                  keyword="size",
                  required=False,
                  convert="size",
                  at=True,
                  text="The quota in MB of the provisioned dataset.",
                  provisioning=True
                )

class KeywordFsMnt(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="fs",
                  keyword="mnt",
                  order=12,
                  at=True,
                  required=True,
                  text="The mount point where to mount the filesystem."
                )

class KeywordFsMntOpt(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="fs",
                  keyword="mnt_opt",
                  order=13,
                  at=True,
                  text="The mount options."
                )

class KeywordFsMkfsOpt(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="fs",
                  keyword="mkfs_opt",
                  convert="shlex",
                  provisioning=True,
                  at=True,
                  order=13,
                  text="Eventual mkfs additional options."
                )

class KeywordFsType(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="fs",
                  keyword="type",
                  order=14,
                  at=True,
                  required=True,
                  strict_candidates=False,
                  candidates=["directory", "zfs", "docker"]+rcEnv.fs_non_pooling,
                  text="The filesystem type or 'directory'. Used to determine the fsck command to use."
                )

class KeywordFsSnapSize(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="fs",
                  keyword="snap_size",
                  order=14,
                  at=True,
                  text="If this filesystem is build on a snapable logical volume or is natively snapable (jfs, vxfs, ...) this setting overrides the default 10% of the filesystem size to compute the snapshot size. The snapshot is created by snap-enabled rsync-type sync resources. The unit is Megabytes."
                )

class KeywordFsDirPath(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="fs",
                  keyword="path",
                  rtype="directory",
                  order=10,
                  at=True,
                  required=True,
                  text="The fullpath of the directory to create."
                )

class KeywordFsDirUser(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="fs",
                  keyword="user",
                  rtype="directory",
                  order=11,
                  at=True,
                  example="root",
                  text="The user that should be owner of the directory. Either in numeric or symbolic form."
                )

class KeywordFsDirGroup(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="fs",
                  keyword="group",
                  rtype="directory",
                  order=11,
                  at=True,
                  example="sys",
                  text="The group that should be owner of the directory. Either in numeric or symbolic form."
                )

class KeywordFsDirPerm(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="fs",
                  keyword="perm",
                  rtype="directory",
                  order=11,
                  at=True,
                  example="1777",
                  text="The permissions the directory should have. A string representing the octal permissions."
                )

class KeywordLoopSize(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="loop",
                  keyword="size",
                  at=True,
                  required=True,
                  default="100m",
                  convert="size",
                  text="The size of the loop file to provision.",
                  provisioning=True
                )

class KeywordLoopFile(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="loop",
                  at=True,
                  keyword="file",
                  required=True,
                  text="The loopback device backing file full path."
                )

class KeywordSyncNetappFiler(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="filer",
                  rtype="netapp",
                  required=True,
                  at=True,
                  text="The Netapp filer resolvable host name used by the node.  Different filers can be set up for each node using the filer@nodename syntax."
                )

class KeywordSyncNetappPath(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="path",
                  rtype="netapp",
                  required=True,
                  text="Specifies the volume or qtree to drive snapmirror on."
                )

class KeywordSyncNetappUser(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="user",
                  rtype="netapp",
                  required=True,
                  default="nasadm",
                  text="Specifies the user used to ssh connect the filers. Nodes should be trusted by keys to access the filer with this user."
                )

class KeywordSyncIbmdssnapPairs(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="pairs",
                  convert="list",
                  at=True,
                  rtype="ibmdssnap",
                  required=True,
                  text="Whitespace-separated list of device pairs.",
                  example="0065:0073 0066:0074"
                )

class KeywordSyncIbmdssnapArray(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="array",
                  at=True,
                  rtype="ibmdssnap",
                  required=True,
                  text="The name of the array holding the source devices and their paired devices.",
                  example="IBM.2243-12ABC00"
                )

class KeywordSyncIbmdssnapBgcopy(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="bgcopy",
                  at=True,
                  rtype="ibmdssnap",
                  candidates=[True, False],
                  required=True,
                  convert="boolean",
                  text="Initiate a background copy of the source data block to the paired devices upon resync."
                )

class KeywordSyncIbmdssnapRecording(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="recording",
                  at=True,
                  rtype="ibmdssnap",
                  candidates=[True, False],
                  required=True,
                  convert="boolean",
                  text="Track only changed data blocks instead of copying the whole source data to the paired devices."
                )

class KeywordSyncNexentaName(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="name",
                  at=True,
                  rtype="nexenta",
                  required=True,
                  text="The name of the Nexenta autosync configuration."
                )

class KeywordSyncNexentaFiler(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="filer",
                  at=True,
                  rtype="nexenta",
                  required=True,
                  text="The name of the Nexenta local head. Must be set for each node using the scoping syntax."
                )

class KeywordSyncNexentaPath(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="path",
                  at=True,
                  rtype="nexenta",
                  required=True,
                  text="The path of the zfs to synchronize, as seen by the Nexenta heads."
                )

class KeywordSyncNexentaReversible(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="reversible",
                  at=True,
                  rtype="nexenta",
                  candidates=[True, False],
                  required=True,
                  text="Defines if the replication link can be reversed. Set to no for prd to drp replications to protect production data."
                )

class KeywordSyncHp3parSnapArray(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="array",
                  rtype="hp3parsnap",
                  required=True,
                  at=True,
                  text="Name of the HP 3par array to send commands to."
                )

class KeywordSyncHp3parSnapVvnames(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="vv_names",
                  convert="list",
                  rtype="hp3parsnap",
                  required=True,
                  at=True,
                  text="The names of snapshot VV or sets of VV to update."
                )

class KeywordSyncHp3parArray(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="array",
                  rtype="hp3par",
                  required=True,
                  at=True,
                  text="Name of the HP 3par array to send commands to."
                )

class KeywordSyncHp3parMode(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="mode",
                  rtype="hp3par",
                  required=True,
                  candidates=["async", "sync"],
                  text="Replication mode: Synchronous or Asynchronous"
                )

class KeywordSyncHp3parMethod(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="method",
                  rtype="hp3par",
                  candidates=["ssh", "cli"],
                  default="ssh",
                  at=True,
                  text="The method to use to submit commands to the arrays."
                )

class KeywordSyncHp3parRcg(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="rcg",
                  rtype="hp3par",
                  required=True,
                  at=True,
                  text="Name of the HP 3par remote copy group. The scoping syntax must be used to fully describe the replication topology."
                )

class KeywordSyncDcsckptDcs(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="dcs",
                  convert="set",
                  rtype="dcsckpt",
                  required=True,
                  text="Whitespace-separated list of DataCore heads, as seen by the manager."
                )

class KeywordSyncDcsckptManager(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="manager",
                  convert="set",
                  rtype="dcsckpt",
                  required=True,
                  text="The DataCore manager name running a ssh daemon, as set in the auth.conf section title."
                )

class KeywordSyncDcsckptPairs(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="pairs",
                  rtype="dcsckpt",
                  required=True,
                  text="A json-formatted list of dictionaries representing the source and destination device pairs. Each dictionary must have the 'src', 'dst_ckpt' keys."
                )

class KeywordSyncDcssnapDcs(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="dcs",
                  rtype="dcssnap",
                  required=True,
                  text="Whitespace-separated list of DataCore heads, as seen by the manager."
                )

class KeywordSyncDcssnapManager(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="manager",
                  rtype="dcssnap",
                  required=True,
                  text="The DataCore manager name running a ssh daemon, as set in the auth.conf section title."
                )

class KeywordSyncDcssnapSnapname(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="snapname",
                  convert="set",
                  rtype="dcssnap",
                  required=True,
                  text="Whitespace-separated list of snapshot device names, as seen by the DataCore manager."
                )

class KeywordSyncEvasnapEvaname(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="eva_name",
                  rtype="evasnap",
                  required=True,
                  text="Name of the HP EVA array hosting the source and snapshot devices."
                )

class KeywordSyncEvasnapSnapname(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="snap_name",
                  rtype="evasnap",
                  required=True,
                  text="Name of the snapshot objectname as seen in sssu."
                )

class KeywordSyncEvasnapPairs(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="pairs",
                  rtype="evasnap",
                  required=True,
                  text="A json-formatted list of dictionaries representing the device pairs. Each dict must have the 'src', 'dst' and 'mask' keys. The mask key value is a list of \\<hostpath>\\<lunid> strings."
                )

class KeywordSyncNecismsnapArray(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="array",
                  rtype="necism",
                  required=True,
                  text="Name of the NEC ISM array to send commands to."
                )

class KeywordSyncNecismsnapDevs(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="devs",
                  rtype="necism",
                  required=True,
                  text="A whitespace-separated list of SV:LD."
                )

class KeywordSyncSymSrdfsSymid(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="symid",
                  at=True,
                  rtype="symsrdfs",
                  required=True,
                  text="Id of the local symmetrix array hosting the symdg. This parameter is usually scoped to define different array ids for different nodes."
                )

class KeywordSyncSymSrdfsSymdg(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="symdg",
                  at=False,
                  rtype="symsrdfs",
                  required=True,
                  text="Name of the symmetrix device group where the source and target devices are grouped."
                )

class KeywordSyncSymSrdfsRdfg(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="rdfg",
                  at=False,
                  rtype="symsrdfs",
                  convert="integer",
                  required=True,
                  text="Name of the RDF group pairing the source and target devices."
                )

class KeywordSyncSymclonePrecopy(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="precopy",
                  at=True,
                  rtype="symclone",
                  default=True,
                  convert="boolean",
                  text="Use -precopy on recreate."
                )

class KeywordSyncSymcloneRecreateTimeout(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="recreate_timeout",
                  at=True,
                  rtype=["symclone", "symsnap"],
                  default=300,
                  convert="duration",
                  text="Maximum wait time for the clone to reach the created state."
                )

class KeywordSyncSymcloneRestoreTimeout(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="restore_timeout",
                  at=True,
                  rtype=["symclone", "symsnap"],
                  default=300,
                  convert="duration",
                  text="Maximum wait time for the clone to reach the restored state."
                )

class KeywordSyncSymcloneSymid(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="symid",
                  rtype=["symclone", "symsnap"],
                  required=True,
                  text="Identifier of the symmetrix array hosting the source and target devices pairs pointed by 'pairs'."
                )

class KeywordSyncSymclonePairs(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="pairs",
                  convert="list",
                  rtype=["symclone", "symsnap"],
                  required=True,
                  at=True,
                  text="Whitespace-separated list of devices <src>:<dst> devid pairs to drive with this resource.",
                  example="00B60:00B61 00B62:00B63",
                )

class KeywordSyncSymcloneConsistent(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="consistent",
                  rtype=["symclone", "symsnap"],
                  at=True,
                  default=True,
                  convert="boolean",
                  text="Use -consistent in symclone commands.",
                )

class KeywordSyncDdsSrc(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="src",
                  rtype="dds",
                  required=True,
                  text="Points the origin of the snapshots to replicate from."
                )

class KeywordSyncDdsDst(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="dst",
                  rtype="dds",
                  at=True,
                  required=True,
                  text="Target file or block device. Optional. Defaults to src. Points the media to replay the binary-delta received from source node to. This media must have a size superior or equal to source."
                )

class KeywordSyncDdsTarget(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="target",
                  convert="list",
                  rtype="dds",
                  required=True,
                  candidates=['nodes', 'drpnodes'],
                  text="Accepted values are 'drpnodes', 'nodes' or both, whitespace-separated. Points the target nodes to replay the binary-deltas on. Be warned that starting the service on a target node without a 'stop-sync_update-start cycle, will break the synchronization, so this mode is usually restricted to drpnodes sync, and should not be used to replicate data between nodes with automated services failover."
                )

class KeywordSyncDdsSnapSize(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="sync",
                  keyword="snap_size",
                  rtype="dds",
                  text="Default to 10% of origin. In MB, rounded to physical extent boundaries by lvm tools. Size of the snapshots created by OpenSVC to extract binary deltas from. Opensvc creates at most 2 snapshots : one short-lived to gather changed data from, and one long-lived to gather changed chunks list from. Volume groups should have the necessary space always available."
                )

class KeywordVdiskPath(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="disk",
                  rtype="vdisk",
                  keyword="path",
                  required=True,
                  at=True,
                  text="Path of the device or file used as a virtual machine disk. The path@nodename can be used to to set up different path on each node."
                )

class KeywordTaskCommand(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="task",
                  keyword="command",
                  at=True,
                  order=1,
                  required=True,
                  text="The command to execute on 'run' action and at scheduled interval. The default schedule for tasks is @0.",
                  example="/srv/{svcname}/data/scripts/backup.sh"
                )

class KeywordTaskConfirmation(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="task",
                  keyword="confirmation",
                  at=True,
                  order=1,
                  default=False,
                  convert="boolean",
                  candidates=(True, False),
                  text="If set to True, ask for an interactive confirmation to run the task. This flag can be used for dangerous tasks like data-restore.",
                )

class KeywordTaskOnError(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="task",
                  keyword="on_error",
                  at=True,
                  order=1,
                  text="A command to execute on 'run' action if 'command' returned an error.",
                  example="/srv/{svcname}/data/scripts/task_on_error.sh"
                )

class KeywordTaskUser(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="task",
                  keyword="user",
                  at=True,
                  order=2,
                  text="The user to impersonate when running the task command. The default user is root.",
                  example="admin"
                )

class KeywordTaskSchedule(Keyword):
    def __init__(self):
        Keyword.__init__(
                  self,
                  section="task",
                  keyword="schedule",
                  default="@0",
                  at=True,
                  text="Set the this task run schedule. See usr/share/doc/node.conf for the schedule syntax reference.",
                  example='["00:00-01:00@61 mon", "02:00-03:00@61 tue-sun"]'
                )

class KeyDict(KeywordStore):
    def __init__(self, provision=False):
        KeywordStore.__init__(
            self, provision,
            deprecated_keywords=DEPRECATED_KEYWORDS,
            deprecated_sections=DEPRECATED_SECTIONS,
            base_sections=["env", "DEFAULT"],
            template_prefix="template.service.",
        )

        def kw_tags(resource):
            return Keyword(
                  section=resource,
                  keyword="tags",
                  convert="set",
                  generic=True,
                  at=True,
                  candidates=None,
                  default=set(),
                  text="A list of tags. Arbitrary tags can be used to limit action scope to resources with a specific tag. Some tags can influence the driver behaviour. For example 'noaction' avoids any state changing action from the driver, 'nostatus' forces the status to n/a."
                )
        def kw_subset(resource):
            return Keyword(
                  section=resource,
                  keyword="subset",
                  inheritance="leaf",
                  generic=True,
                  at=True,
                  text="Assign the resource to a specific subset."
                )
        def kw_restart(resource):
            return Keyword(
                  section=resource,
                  keyword="restart",
                  generic=True,
                  at=True,
                  default=0,
                  convert="integer",
                  text="The agent will try to restart a resource n times before falling back to the monitor action."
                )
        def kw_provision(resource):
            return Keyword(
                  section=resource,
                  keyword="provision",
                  generic=True,
                  at=True,
                  candidates=(True, False),
                  default=True,
                  convert="boolean",
                  text="Set to false to skip the resource on provision and unprovision actions. Warning: provisioning implies destructive operations like formating. Unprovisioning does not keep a copy of the service data."
                )
        def kw_unprovision(resource):
            return Keyword(
                  section=resource,
                  keyword="unprovision",
                  generic=True,
                  at=True,
                  candidates=(True, False),
                  default=True,
                  convert="boolean",
                  text="Set to false to skip the resource on unprovision actions. Warning: Unprovisioning does not keep a copy of the service data."
                )
        def kw_shared(resource):
            return Keyword(
                  section=resource,
                  keyword="shared",
                  generic=True,
                  at=True,
                  candidates=(True, False),
                  default=False,
                  convert="boolean",
                  text="Set to True to skip the resource on provision and unprovision actions if the action has already been done by a peer. Shared resources, like vg built on SAN disks must be provisioned once. All resources depending on a shared resource must also be flagged as shared."
                )
        def kw_encap(resource):
            return Keyword(
                  section=resource,
                  keyword="encap",
                  generic=True,
                  at=True,
                  candidates=(True, False),
                  default=False,
                  convert="boolean",
                  text="Set to True to ignore this resource in the nodes context and consider it in the encapnodes context. The resource is thus handled by the agents deployed in the service containers."
                )
        def kw_monitor(resource):
            return Keyword(
                  section=resource,
                  keyword="monitor",
                  generic=True,
                  at=True,
                  candidates=(True, False),
                  default=False,
                  convert="boolean",
                  text="A down monitored resource will trigger a node suicide if the monitor thinks it should be up and the resource can not be restarted."
                )
        def kw_disable(resource):
            return Keyword(
                  section=resource,
                  keyword="disable",
                  inheritance="leaf",
                  generic=True,
                  at=True,
                  candidates=(True, False),
                  default=False,
                  convert="boolean",
                  text="A disabled resource will be ignored on service startup and shutdown. Its status is always reported ``n/a``.\n\nSet in DEFAULT, the whole service is disabled. A disabled service does not honor start and stop actions. These actions immediately return success.\n\n:cmd:`sudo svcmgr -s <svcname> disable` only sets :kw:`DEFAULT.disable`. As resources disabled state is not changed, :cmd:`sudo svcmgr -s <svcname> enable` does not enable disabled resources."
                )
        def kw_optional(resource):
            return Keyword(
                  section=resource,
                  keyword="optional",
                  generic=True,
                  at=True,
                  candidates=(True, False),
                  default_text="True for task and sync, else False",
                  convert="boolean",
                  text="Possible values are 'true' or 'false'. Actions on resource will be tried upon service startup and shutdown, but action failures will be logged and passed over. Useful for resources like dump filesystems for example."
                )
        def kw_standby(resource):
            return Keyword(
                  section=resource,
                  keyword="standby",
                  generic=True,
                  at=True,
                  default=False,
                  candidates=(True, False),
                  convert="boolean",
                  text="Always start the resource, even on standby instances. The daemon is responsible for starting standby resources. A resource can be set standby on a subset of nodes using keyword scoping.\n\nA typical use-case is sync'ed fs on non-shared disks: the remote fs must be mounted to not overflow the underlying fs.\n\n.. warning:: Don't set shared resources standby: fs on shared disks for example."
                )
        def kw_always_on(resource):
            return Keyword(
                  section=resource,
                  keyword="always_on",
                  generic=True,
                  convert="list",
                  default=[],
                  default_text="",
                  candidates=['nodes', 'drpnodes'],
                  strict_candidates=False,
                  text="Possible values are 'nodes', 'drpnodes' or 'nodes drpnodes', or a list of nodes. Sets the nodes on which the resource is always kept up. Primary usage is file synchronization receiving on non-shared disks. Don't set this on shared disk !! danger !!"
                )
        def kw_pre_unprovision(resource):
            return Keyword(
                  section=resource,
                  keyword="pre_unprovision",
                  generic=True,
                  at=True,
                  text="A command or script to execute before the resource unprovision action. Errors do not interrupt the action."
                )
        def kw_post_unprovision(resource):
            return Keyword(
                  section=resource,
                  keyword="post_unprovision",
                  generic=True,
                  at=True,
                  text="A command or script to execute after the resource unprovision action. Errors do not interrupt the action."
                )
        def kw_pre_provision(resource):
            return Keyword(
                  section=resource,
                  keyword="pre_provision",
                  generic=True,
                  at=True,
                  text="A command or script to execute before the resource provision action. Errors do not interrupt the action."
                )
        def kw_post_provision(resource):
            return Keyword(
                  section=resource,
                  keyword="post_provision",
                  generic=True,
                  at=True,
                  text="A command or script to execute after the resource provision action. Errors do not interrupt the action."
                )
        def kw_pre_start(resource):
            return Keyword(
                  section=resource,
                  keyword="pre_start",
                  generic=True,
                  at=True,
                  text="A command or script to execute before the resource start action. Errors do not interrupt the action."
                )
        def kw_post_start(resource):
            return Keyword(
                  section=resource,
                  keyword="post_start",
                  generic=True,
                  at=True,
                  text="A command or script to execute after the resource start action. Errors do not interrupt the action."
                )
        def kw_pre_stop(resource):
            return Keyword(
                  section=resource,
                  keyword="pre_stop",
                  generic=True,
                  at=True,
                  text="A command or script to execute before the resource stop action. Errors do not interrupt the action."
                )
        def kw_post_stop(resource):
            return Keyword(
                  section=resource,
                  keyword="post_stop",
                  generic=True,
                  at=True,
                  text="A command or script to execute after the resource stop action. Errors do not interrupt the action."
                )
        def kw_pre_sync_nodes(resource):
            return Keyword(
                  section=resource,
                  keyword="pre_sync_nodes",
                  generic=True,
                  at=True,
                  text="A command or script to execute before the resource sync_nodes action. Errors do not interrupt the action."
                )
        def kw_post_sync_nodes(resource):
            return Keyword(
                  section=resource,
                  keyword="post_sync_nodes",
                  generic=True,
                  at=True,
                  text="A command or script to execute after the resource sync_nodes action. Errors do not interrupt the action."
                )
        def kw_pre_sync_drp(resource):
            return Keyword(
                  section=resource,
                  keyword="pre_sync_drp",
                  generic=True,
                  at=True,
                  text="A command or script to execute before the resource sync_drp action. Errors do not interrupt the action."
                )
        def kw_post_sync_drp(resource):
            return Keyword(
                  section=resource,
                  keyword="post_sync_drp",
                  generic=True,
                  at=True,
                  text="A command or script to execute after the resource sync_drp action. Errors do not interrupt the action."
                )
        def kw_pre_sync_restore(resource):
            return Keyword(
                  section=resource,
                  keyword="pre_sync_restore",
                  generic=True,
                  at=True,
                  text="A command or script to execute before the resource sync_restore action. Errors do not interrupt the action."
                )
        def kw_post_sync_restore(resource):
            return Keyword(
                  section=resource,
                  keyword="post_sync_restore",
                  generic=True,
                  at=True,
                  text="A command or script to execute after the resource sync_restore action. Errors do not interrupt the action."
                )
        def kw_pre_sync_resync(resource):
            return Keyword(
                  section=resource,
                  keyword="pre_sync_resync",
                  generic=True,
                  at=True,
                  text="A command or script to execute before the resource sync_resync action. Errors do not interrupt the action."
                )
        def kw_post_sync_resync(resource):
            return Keyword(
                  section=resource,
                  keyword="post_sync_resync",
                  generic=True,
                  at=True,
                  text="A command or script to execute after the resource sync_resync action. Errors do not interrupt the action."
                )
        def kw_pre_sync_update(resource):
            return Keyword(
                  section=resource,
                  keyword="pre_sync_update",
                  generic=True,
                  at=True,
                  text="A command or script to execute before the resource sync_update action. Errors do not interrupt the action."
                )
        def kw_post_sync_update(resource):
            return Keyword(
                  section=resource,
                  keyword="post_sync_update",
                  generic=True,
                  at=True,
                  text="A command or script to execute after the resource sync_update action. Errors do not interrupt the action."
                )
        def kw_pre_run(resource):
            return Keyword(
                  section=resource,
                  keyword="pre_run",
                  generic=True,
                  at=True,
                  text="A command or script to execute before the resource run action. Errors do not interrupt the action."
                )
        def kw_post_run(resource):
            return Keyword(
                  section=resource,
                  keyword="post_run",
                  generic=True,
                  at=True,
                  text="A command or script to execute after the resource run action. Errors do not interrupt the action."
                )

        def kw_blocking_pre_unprovision(resource):
            return Keyword(
                  section=resource,
                  keyword="blocking_pre_unprovision",
                  generic=True,
                  at=True,
                  text="A command or script to execute before the resource unprovision action. Errors interrupt the action."
                )
        def kw_blocking_post_unprovision(resource):
            return Keyword(
                  section=resource,
                  keyword="blocking_post_unprovision",
                  generic=True,
                  at=True,
                  text="A command or script to execute after the resource unprovision action. Errors interrupt the action."
                )
        def kw_blocking_pre_provision(resource):
            return Keyword(
                  section=resource,
                  keyword="blocking_pre_provision",
                  generic=True,
                  at=True,
                  text="A command or script to execute before the resource provision action. Errors interrupt the action."
                )
        def kw_blocking_post_provision(resource):
            return Keyword(
                  section=resource,
                  keyword="blocking_post_provision",
                  generic=True,
                  at=True,
                  text="A command or script to execute after the resource provision action. Errors interrupt the action."
                )
        def kw_blocking_pre_start(resource):
            return Keyword(
                  section=resource,
                  keyword="blocking_pre_start",
                  generic=True,
                  at=True,
                  text="A command or script to execute before the resource start action. Errors interrupt the action."
                )
        def kw_blocking_post_start(resource):
            return Keyword(
                  section=resource,
                  keyword="blocking_post_start",
                  generic=True,
                  at=True,
                  text="A command or script to execute after the resource start action. Errors interrupt the action."
                )
        def kw_blocking_pre_stop(resource):
            return Keyword(
                  section=resource,
                  keyword="blocking_pre_stop",
                  generic=True,
                  at=True,
                  text="A command or script to execute before the resource stop action. Errors interrupt the action."
                )
        def kw_blocking_post_stop(resource):
            return Keyword(
                  section=resource,
                  keyword="blocking_post_stop",
                  generic=True,
                  at=True,
                  text="A command or script to execute after the resource stop action. Errors interrupt the action."
                )
        def kw_blocking_pre_sync_nodes(resource):
            return Keyword(
                  section=resource,
                  keyword="blocking_pre_sync_nodes",
                  generic=True,
                  at=True,
                  text="A command or script to execute before the resource sync_nodes action. Errors interrupt the action."
                )
        def kw_blocking_post_sync_nodes(resource):
            return Keyword(
                  section=resource,
                  keyword="blocking_post_sync_nodes",
                  generic=True,
                  at=True,
                  text="A command or script to execute after the resource sync_nodes action. Errors interrupt the action."
                )
        def kw_blocking_pre_sync_drp(resource):
            return Keyword(
                  section=resource,
                  keyword="blocking_pre_sync_drp",
                  generic=True,
                  at=True,
                  text="A command or script to execute before the resource sync_drp action. Errors interrupt the action."
                )
        def kw_blocking_post_sync_drp(resource):
            return Keyword(
                  section=resource,
                  keyword="blocking_post_sync_drp",
                  generic=True,
                  at=True,
                  text="A command or script to execute after the resource sync_drp action. Errors interrupt the action."
                )
        def kw_blocking_pre_sync_restore(resource):
            return Keyword(
                  section=resource,
                  keyword="blocking_pre_sync_restore",
                  generic=True,
                  at=True,
                  text="A command or script to execute before the resource sync_restore action. Errors interrupt the action."
                )
        def kw_blocking_post_sync_restore(resource):
            return Keyword(
                  section=resource,
                  keyword="blocking_post_sync_restore",
                  generic=True,
                  at=True,
                  text="A command or script to execute after the resource sync_restore action. Errors interrupt the action."
                )
        def kw_blocking_pre_sync_resync(resource):
            return Keyword(
                  section=resource,
                  keyword="blocking_pre_sync_resync",
                  generic=True,
                  at=True,
                  text="A command or script to execute before the resource sync_resync action. Errors interrupt the action."
                )
        def kw_blocking_post_sync_resync(resource):
            return Keyword(
                  section=resource,
                  keyword="blocking_post_sync_resync",
                  generic=True,
                  at=True,
                  text="A command or script to execute after the resource sync_resync action. Errors interrupt the action."
                )
        def kw_blocking_pre_sync_update(resource):
            return Keyword(
                  section=resource,
                  keyword="blocking_pre_sync_update",
                  generic=True,
                  at=True,
                  text="A command or script to execute before the resource sync_update action. Errors interrupt the action."
                )
        def kw_blocking_post_sync_update(resource):
            return Keyword(
                  section=resource,
                  keyword="blocking_post_sync_update",
                  generic=True,
                  at=True,
                  text="A command or script to execute after the resource sync_update action. Errors interrupt the action."
                )
        def kw_blocking_pre_run(resource):
            return Keyword(
                  section=resource,
                  keyword="blocking_pre_run",
                  generic=True,
                  at=True,
                  text="A command or script to execute before the resource run action. Errors interrupt the action."
                )
        def kw_blocking_post_run(resource):
            return Keyword(
                  section=resource,
                  keyword="blocking_post_run",
                  generic=True,
                  at=True,
                  text="A command or script to execute after the resource run action. Errors interrupt the action."
                )

        def kw_requires(section, action):
            return Keyword(
                section=section,
                keyword=action+"_requires",
                generic=True,
                at=True,
                example="ip#0 fs#0(down,stdby down)",
                default="",
                text="A whitespace-separated list of conditions to meet to accept running a '%s' action. A condition is expressed as <rid>(<state>,...). If states are omitted, 'up,stdby up' is used as the default expected states." % action
            )

        def kw_pg_cpus(resource):
            return Keyword(
                section=resource,
                keyword="pg_cpus",
                generic=True,
                inheritance="leaf",
                at=True,
                order=31,
                depends=[('create_pg', [True])],
                text="Allow service process to bind only the specified cpus. Cpus are specified as list or range : 0,1,2 or 0-2",
                example="0-2"
            )

        def kw_pg_mems(resource):
            return Keyword(
                section=resource,
                keyword="pg_mems",
                generic=True,
                inheritance="leaf",
                at=True,
                order=31,
                depends=[('create_pg', [True])],
                text="Allow service process to bind only the specified memory nodes. Memory nodes are specified as list or range : 0,1,2 or 0-2",
                example="0-2"
            )

        def kw_pg_cpu_share(resource):
            return Keyword(
                section=resource,
                keyword="pg_cpu_shares",
                generic=True,
                inheritance="leaf",
                at=True,
                convert="integer",
                order=31,
                depends=[('create_pg', [True])],
                text="Kernel default value is used, which usually is 1024 shares. In a cpu-bound situation, ensure the service does not use more than its share of cpu ressource. The actual percentile depends on shares allowed to other services.",
                example="512"
            )

        def kw_pg_cpu_quota(resource):
            return Keyword(
                section=resource,
                keyword="pg_cpu_quota",
                generic=True,
                inheritance="leaf",
                at=True,
                order=31,
                depends=[('create_pg', [True])],
                text="The percent ratio of one core to allocate to the process group if % is specified, else the absolute value to set in the process group parameter. For example, on Linux cgroups, -1 means unlimited, and a positive absolute value means the number of microseconds to allocate each period. 50%@all means 50% of all cores, and 50%@2 means 50% of two cores.",
                example="50%@all"
            )

        def kw_pg_mem_oom_control(resource):
            return Keyword(
                section=resource,
                keyword="pg_mem_oom_control",
                generic=True,
                inheritance="leaf",
                at=True,
                convert="integer",
                order=31,
                depends=[('create_pg', [True])],
                text="A flag (0 or 1) that enables or disables the Out of Memory killer for a cgroup. If enabled (0), tasks that attempt to consume more memory than they are allowed are immediately killed by the OOM killer. The OOM killer is enabled by default in every cgroup using the memory subsystem; to disable it, write 1.",
                example="1"
            )

        def kw_pg_mem_limit(resource):
            return Keyword(
                section=resource,
                keyword="pg_mem_limit",
                generic=True,
                inheritance="leaf",
                at=True,
                convert="size",
                order=31,
                depends=[('create_pg', [True])],
                text="Ensures the service does not use more than specified memory (in bytes). The Out-Of-Memory killer get triggered in case of tresspassing.",
                example="512000000"
            )

        def kw_pg_vmem_limit(resource):
            return Keyword(
                section=resource,
                keyword="pg_vmem_limit",
                generic=True,
                inheritance="leaf",
                at=True,
                convert="size",
                order=31,
                depends=[('create_pg', [True])],
                text="Ensures the service does not use more than specified memory+swap (in bytes). The Out-Of-Memory killer get triggered in case of tresspassing. The specified value must be greater than pg_mem_limit.",
                example="1024000000"
            )

        def kw_pg_mem_swappiness(resource):
            return Keyword(
                section=resource,
                keyword="pg_mem_swappiness",
                generic=True,
                inheritance="leaf",
                at=True,
                convert="integer",
                order=31,
                depends=[('create_pg', [True])],
                text="Set a swappiness value for the process group.",
                example="40"
            )

        def kw_pg_blkio_weight(resource):
            return Keyword(
                section=resource,
                keyword="pg_blkio_weight",
                generic=True,
                inheritance="leaf",
                at=True,
                convert="integer",
                order=31,
                depends=[('create_pg', [True])],
                text="Block IO relative weight. Value: between 10 and 1000. Kernel default: 1000.",
                example="50"
            )


        for r in ["DEFAULT", "sync", "ip", "fs", "disk", "share", "container", "app", "task"]:
            self += kw_restart(r)
            self += kw_tags(r)
            self += kw_subset(r)
            self += kw_monitor(r)
            self += kw_disable(r)
            self += kw_optional(r)
            self += kw_always_on(r)
            self += kw_standby(r)
            self += kw_provision(r)
            self += kw_unprovision(r)
            self += kw_shared(r)
            self += kw_encap(r)

            self += kw_pre_unprovision(r)
            self += kw_post_unprovision(r)
            self += kw_pre_provision(r)
            self += kw_post_provision(r)
            self += kw_pre_start(r)
            self += kw_post_start(r)
            self += kw_pre_stop(r)
            self += kw_post_stop(r)

            self += kw_blocking_pre_unprovision(r)
            self += kw_blocking_post_unprovision(r)
            self += kw_blocking_pre_provision(r)
            self += kw_blocking_post_provision(r)
            self += kw_blocking_pre_start(r)
            self += kw_blocking_post_start(r)
            self += kw_blocking_pre_stop(r)
            self += kw_blocking_post_stop(r)

            for action in ["unprovision", "provision", "start", "stop"]:
                self += kw_requires(r, action)

        for r in ["DEFAULT", "sync", "ip", "fs", "disk", "share", "container", "app", "task", "subset"]:
            self += kw_pg_cpus(r)
            self += kw_pg_mems(r)
            self += kw_pg_cpu_share(r)
            self += kw_pg_cpu_quota(r)
            self += kw_pg_mem_oom_control(r)
            self += kw_pg_mem_limit(r)
            self += kw_pg_mem_swappiness(r)
            self += kw_pg_vmem_limit(r)
            self += kw_pg_blkio_weight(r)

        for r in ["DEFAULT", "sync"]:
            self += kw_pre_sync_nodes(r)
            self += kw_post_sync_nodes(r)
            self += kw_pre_sync_drp(r)
            self += kw_post_sync_drp(r)
            self += kw_pre_sync_restore(r)
            self += kw_post_sync_restore(r)
            self += kw_pre_sync_resync(r)
            self += kw_post_sync_resync(r)
            self += kw_pre_sync_update(r)
            self += kw_post_sync_update(r)

            self += kw_blocking_pre_sync_nodes(r)
            self += kw_blocking_post_sync_nodes(r)
            self += kw_blocking_pre_sync_drp(r)
            self += kw_blocking_post_sync_drp(r)
            self += kw_blocking_pre_sync_restore(r)
            self += kw_blocking_post_sync_restore(r)
            self += kw_blocking_pre_sync_resync(r)
            self += kw_blocking_post_sync_resync(r)
            self += kw_blocking_pre_sync_update(r)
            self += kw_blocking_post_sync_update(r)

            for action in ["sync_nodes", "sync_drp", "sync_update",
                           "sync_break", "sync_resync", "sync_restore"]:
                self += kw_requires(r, action)

        for r in ["DEFAULT", "task"]:
            self += kw_pre_run(r)
            self += kw_post_run(r)

            self += kw_blocking_pre_run(r)
            self += kw_blocking_post_run(r)

            for action in ["run"]:
                self += kw_requires(r, action)


        self += KeywordMode()
        self += KeywordLockTimeout()
        self += KeywordPrKey()
        self += KeywordDockerDaemonPrivate()
        self += KeywordDockerExe()
        self += KeywordDockerdExe()
        self += KeywordDockerDataDir()
        self += KeywordDockerDaemonArgs()
        self += KeywordDockerSwarmArgs()
        self += KeywordDockerSwarmManagers()
        self += KeywordHardAffinity()
        self += KeywordHardAntiAffinity()
        self += KeywordSoftAffinity()
        self += KeywordSoftAntiAffinity()
        self += KeywordShowDisabled()
        self += KeywordTopology()
        self += KeywordScale()
        self += KeywordScalerSlave()
        self += KeywordOrchestrate()
        self += KeywordPlacement()
        self += KeywordConstraints()
        self += KeywordParents()
        self += KeywordChildren()
        self += KeywordSlaves()
        self += KeywordFlexPrimary()
        self += KeywordDrpFlexPrimary()
        self += KeywordRollback()
        self += KeywordStatusSchedule()
        self += KeywordCompSchedule()
        self += KeywordMonitorSchedule()
        self += KeywordResinfoSchedule()
        self += KeywordPushSchedule()
        self += KeywordFlexMinNodes()
        self += KeywordFlexMaxNodes()
        self += KeywordFlexCpuLowThreshold()
        self += KeywordFlexCpuHighThreshold()
        self += KeywordServiceEnv()
        self += KeywordNodes()
        self += KeywordDrpnode()
        self += KeywordDrpnodes()
        self += KeywordEncapnodes()
        self += KeywordApp()
        self += KeywordComment()
        self += KeywordDefaultScsireserv()
        self += KeywordDefaultNoPreemptAbort()
        self += KeywordBwlimit()
        self += KeywordSyncInterval()
        self += KeywordSyncMaxDelay()
        self += KeywordPresnapTrigger()
        self += KeywordPostsnapTrigger()
        self += KeywordPreMonitorAction()
        self += KeywordMonitorAction()
        self += KeywordCreatePg()
        self += KeywordSyncType()
        self += KeywordSyncDockerTarget()
        self += KeywordSyncBtrfsSrc()
        self += KeywordSyncBtrfsDst()
        self += KeywordSyncBtrfsTarget()
        self += KeywordSyncBtrfsRecursive()
        self += KeywordSyncBtrfsSnapName()
        self += KeywordSyncBtrfsSnapSubvol()
        self += KeywordSyncBtrfsSnapKeep()
        self += KeywordSyncZfsSnapName()
        self += KeywordSyncZfsSnapRecursive()
        self += KeywordSyncZfsSnapDataset()
        self += KeywordSyncZfsSnapKeep()
        self += KeywordSyncS3Snar()
        self += KeywordSyncS3Src()
        self += KeywordSyncS3Options()
        self += KeywordSyncS3Bucket()
        self += KeywordSyncS3FullSchedule()
        self += KeywordSyncZfsSrc()
        self += KeywordSyncZfsDst()
        self += KeywordSyncZfsTarget()
        self += KeywordSyncZfsRecursive()
        self += KeywordSyncZfsTags()
        self += KeywordSyncRsyncSrc()
        self += KeywordSyncRsyncDst()
        self += KeywordSyncRsyncTags()
        self += KeywordSyncRsyncOptions()
        self += KeywordSyncRsyncTarget()
        self += KeywordSyncRsyncSnap()
        self += KeywordSyncRsyncDstfs()
        self += KeywordSyncRsyncBwlimit()
        self += KeywordDefaultSyncSchedule()
        self += KeywordDefaultAws()
        self += KeywordDefaultAwsProfile()
        self += KeywordSyncSchedule()
        self += KeywordSyncSyncMaxDelay()
        self += KeywordIpType()
        self += KeywordIpIpname()
        self += KeywordIpIpdev()
        self += KeywordIpIpdevext()
        self += KeywordIpDelNetRoute()
        self += KeywordIpNetmask()
        self += KeywordIpGateway()
        self += KeywordIpProvisioner()
        self += KeywordIpDnsUpdate()
        self += KeywordIpDnsNameSuffix()
        self += KeywordIpNetwork()
        self += KeywordIpZone()
        self += KeywordIpExpose()
        self += KeywordIpCniNetwork()
        self += KeywordIpCniContainerRid()
        self += KeywordIpDockerContainerRid()
        self += KeywordIpAmazonEip()
        self += KeywordIpAmazonCascadeAllocation()
        self += KeywordIpAmazonDockerDaemonIp()
        self += KeywordIpGceZone()
        self += KeywordIpGceRoutename()
        self += KeywordDiskPrKey()
        self += KeywordDiskGceNames()
        self += KeywordDiskGceZone()
        self += KeywordDiskGceDescription()
        self += KeywordDiskGceImage()
        self += KeywordDiskGceImageProject()
        self += KeywordDiskGceSize()
        self += KeywordDiskGceSourceSnapshot()
        self += KeywordDiskGceDiskType()
        self += KeywordDiskType()
        self += KeywordDiskDiskDiskId()
        self += KeywordDiskDiskSize()
        self += KeywordDiskDiskArray()
        self += KeywordDiskDiskDiskGroup()
        self += KeywordDiskDiskSlo()
        self += KeywordDiskAmazonVolumes()
        self += KeywordDiskRawDevs()
        self += KeywordDiskRawZone()
        self += KeywordDiskRawCreateCharDevices()
        self += KeywordDiskRawUser()
        self += KeywordDiskRawGroup()
        self += KeywordDiskRawPerm()
        self += KeywordDiskVgName()
        self += KeywordDiskDsf()
        self += KeywordDiskImages()
        self += KeywordDiskMdUuid()
        self += KeywordDiskMdDevs()
        self += KeywordDiskMdLevel()
        self += KeywordDiskMdChunk()
        self += KeywordDiskMdLayout()
        self += KeywordDiskMdSpares()
        self += KeywordFsDockerDriver()
        self += KeywordFsDockerOptions()
        self += KeywordFsPopulate()
        self += KeywordDiskLvName()
        self += KeywordDiskLvVg()
        self += KeywordDiskLvSize()
        self += KeywordDiskLvCreateOptions()
        self += KeywordDiskFsCreateOptions()
        self += KeywordSyncRadosPairs()
        self += KeywordSyncRadosImages()
        self += KeywordDiskClientId()
        self += KeywordDiskKeyring()
        self += KeywordDiskLock()
        self += KeywordDiskLockSharedTag()
        self += KeywordDiskSize()
        self += KeywordDiskImageFormat()
        self += KeywordDiskOptions()
        self += KeywordDiskPromoteRW()
        self += KeywordDiskScsireserv()
        self += KeywordDiskPromoteRW()
        self += KeywordDiskNoPreemptAbort()
        self += KeywordDiskPvs()
        self += KeywordZPoolName()
        self += KeywordZPoolVdev()
        self += KeywordZPoolZone()
        self += KeywordVmdgContainerid()
        self += KeywordDiskDrbdRes()
        self += KeywordFsType()
        self += KeywordFsDev()
        self += KeywordFsZone()
        self += KeywordFsMnt()
        self += KeywordFsMntOpt()
        self += KeywordFsMkfsOpt()
        self += KeywordFsSnapSize()
        self += KeywordFsVg()
        self += KeywordFsPromoteRW()
        self += KeywordFsScsireserv()
        self += KeywordFsPromoteRW()
        self += KeywordFsNoPreemptAbort()
        self += KeywordFsSize()
        self += KeywordFsZfsSize()
        self += KeywordFsDirPath()
        self += KeywordFsDirUser()
        self += KeywordFsDirGroup()
        self += KeywordFsDirPerm()
        self += KeywordLoopFile()
        self += KeywordLoopSize()
        self += KeywordAppScript()
        self += KeywordAppLimitAs()
        self += KeywordAppLimitCore()
        self += KeywordAppLimitCpu()
        self += KeywordAppLimitData()
        self += KeywordAppLimitFsize()
        self += KeywordAppLimitMemlock()
        self += KeywordAppLimitNofile()
        self += KeywordAppLimitNproc()
        self += KeywordAppLimitRss()
        self += KeywordAppLimitStack()
        self += KeywordAppLimitVmem()
        self += KeywordAppTimeout()
        self += KeywordAppStartTimeout()
        self += KeywordAppStopTimeout()
        self += KeywordAppCheckTimeout()
        self += KeywordAppInfoTimeout()
        self += KeywordAppStart()
        self += KeywordAppStop()
        self += KeywordAppCheck()
        self += KeywordAppInfo()
        self += KeywordSyncNexentaName()
        self += KeywordSyncNexentaFiler()
        self += KeywordSyncNexentaPath()
        self += KeywordSyncNexentaReversible()
        self += KeywordSyncNetappFiler()
        self += KeywordSyncNetappPath()
        self += KeywordSyncNetappUser()
        self += KeywordSyncIbmdssnapPairs()
        self += KeywordSyncIbmdssnapArray()
        self += KeywordSyncIbmdssnapBgcopy()
        self += KeywordSyncIbmdssnapRecording()
        self += KeywordSyncSymSrdfsSymid()
        self += KeywordSyncSymSrdfsSymdg()
        self += KeywordSyncSymSrdfsRdfg()
        self += KeywordSyncSymcloneConsistent()
        self += KeywordSyncSymcloneSymid()
        self += KeywordSyncSymcloneRestoreTimeout()
        self += KeywordSyncSymcloneRecreateTimeout()
        self += KeywordSyncSymclonePairs()
        self += KeywordSyncSymclonePrecopy()
        self += KeywordSyncDcsckptDcs()
        self += KeywordSyncDcsckptManager()
        self += KeywordSyncDcsckptPairs()
        self += KeywordSyncDcssnapDcs()
        self += KeywordSyncDcssnapManager()
        self += KeywordSyncDcssnapSnapname()
        self += KeywordSyncNecismsnapArray()
        self += KeywordSyncNecismsnapDevs()
        self += KeywordSyncEvasnapEvaname()
        self += KeywordSyncEvasnapSnapname()
        self += KeywordSyncEvasnapPairs()
        self += KeywordSyncHp3parArray()
        self += KeywordSyncHp3parRcg()
        self += KeywordSyncHp3parMode()
        self += KeywordSyncHp3parMethod()
        self += KeywordSyncHp3parSnapArray()
        self += KeywordSyncHp3parSnapVvnames()
        self += KeywordSyncDdsSrc()
        self += KeywordSyncDdsDst()
        self += KeywordSyncDdsTarget()
        self += KeywordSyncDdsSnapSize()
        self += KeywordVdiskPath()
        self += KeywordSubsetParallel()
        self += KeywordStonith()
        self += KeywordContainerType()
        self += KeywordContainerZoneDeleteOnStop()
        self += KeywordContainerPromoteRW()
        self += KeywordContainerScsireserv()
        self += KeywordContainerPromoteRW()
        self += KeywordContainerNoPreemptAbort()
        self += KeywordContainerOrigin()
        self += KeywordContainerRcmd()
        self += KeywordContainerVapp()
        self += KeywordVmName()
        self += KeywordVmHostname()
        self += KeywordOsvcRootPath()
        self += KeywordGuestos()
        self += KeywordRootfs()
        self += KeywordLxcCf()
        self += KeywordContainerLxdLaunchImage()
        self += KeywordContainerLxdLaunchOptions()
        self += KeywordJailRoot()
        self += KeywordJailIps()
        self += KeywordJailIp6s()
        self += KeywordTemplate()
        self += KeywordSharedIpGroup()
        self += KeywordSize()
        self += KeywordKeyName()
        self += KeywordCloudId()
        self += KeywordVmUuid()
        self += KeywordVirtinst()
        self += KeywordDockerDockerService()
        self += KeywordDockerRunCommand()
        self += KeywordDockerRunImage()
        self += KeywordDockerRunArgs()
        self += KeywordSnap()
        self += KeywordSnapof()
        self += KeywordSrpIp()
        self += KeywordSrpRootpath()
        self += KeywordSrpPrmCores()
        self += KeywordShareType()
        self += KeywordSharePath()
        self += KeywordShareNfsOpts()
        self += KeywordTaskCommand()
        self += KeywordTaskConfirmation()
        self += KeywordTaskOnError()
        self += KeywordTaskUser()
        self += KeywordTaskSchedule()

KEYS = KeyDict(provision=True)

if __name__ == "__main__":
    if len(sys.argv) == 2:
        fmt = sys.argv[1]
    else:
        fmt = "text"

    KEYS.print_templates(fmt=fmt)
    #print(KEYS.container.getkey("cf"))
    #print(KEYS['DEFAULT'])
  0707010005ca12000081b40000000000000000000000015ab217540000255d000000660000000000000000000000000000002500000000root/usr/share/opensvc/lib/svcmgr.py  # coding: utf8

"""
This executable is wrapped by the opensvc shell script.

It's the entrypoint for all OpenSVC services management ops.
"""
from __future__ import print_function
from __future__ import absolute_import

import sys
import os
import errno

import svcBuilder
import rcStatus
import rcColor
from svcmgr_parser import SvcmgrOptParser
import rcExceptions as ex
from rcUtilities import ximport, check_privs
from rcGlobalEnv import Storage

def get_docker_argv(argv=None):
    """
    Extract docker argv from svcmgr argv.

    svcmgr acts as a wrapper for docker, setting the service-specific socket
    if necessary.
    """
    if argv is None:
        argv = sys.argv[1:]
    if len(argv) < 2:
        return argv, []
    if "docker" not in argv:
        return argv, []
    pos = argv.index('docker')
    if len(argv) > pos + 1:
        docker_argv = argv[pos+1:]
    else:
        docker_argv = []
    argv = argv[:pos+1]
    return argv, docker_argv

def get_minimal(action, options):
    """
    Return True if the services can be built with minimal parsing
    """
    if action == "ls" and not options.status:
        return True
    if action == "get" and not options.eval:
        return True
    if action == "edit_config":
        return True
    if action.startswith("print_config"):
        return True
    if action.startswith("json_config"):
        return True
    if action.startswith("collector_"):
        return True
    return False

def get_build_kwargs(optparser, options, action):
    """
    Return the service build function keyword arguments, deduced from
    parsed command line options.
    """
    build_kwargs = {}
    build_kwargs["minimal"] = get_minimal(action, options)

    if len(set(["svcnames", "status"]) & set(build_kwargs.keys())) == 0:
        if os.environ.get("OSVC_SERVICE_LINK"):
            build_kwargs["svcnames"] = [os.environ.get("OSVC_SERVICE_LINK")]
        if hasattr(options, "svcs") and options.svcs is not None:
            build_kwargs["svcnames"] = options.svcs

    if hasattr(options, "status") and options.status is not None:
        build_kwargs["status"] = [rcStatus.status_value(s) for s in options.status.split(",")]

    build_kwargs["create_instance"] = action in ("create", "pull")

    return build_kwargs

def do_svcs_action_detached(argv=None):
    """
    Executes the services action in detached process mode, so that
    a term/kill signal on the parent process does not abort the action.

    Keyboard interrupts do abort the detached process though.
    """
    ret = 0
    try:
        import subprocess
        import signal
        proc = subprocess.Popen([sys.executable, __file__] + argv + ["--daemon"],
                                stdout=None, stderr=None, stdin=None,
                                close_fds=True, cwd=os.sep,
                                preexec_fn=os.setsid)
        proc.wait()
        ret = proc.returncode
    except KeyboardInterrupt as exc:
        os.killpg(os.getpgid(proc.pid), signal.SIGKILL)
        print("kill detached process")
        ret = 1
    except ex.excSignal as exc:
        print("the action, detached as pid %d, "
              "will continue executing" % proc.pid)
        ret = 1
    except Exception as exc:
        print(exc, file=sys.stderr)
        ret = 1
    return ret

def do_svcs_action(node, options, action, argv):
    """
    Execute the services action, switching between detached mode for
    stop*/shutdown/unprovision/switch, and inline mode for other actions.
    """
    ret = 0

    if os.environ.get("OSVC_ACTION_ORIGIN") != "daemon" and \
       not options.daemon and ( \
        action in ("stop", "shutdown", "unprovision", "switch") or \
        (action == "delete" and options.unprovision == True)
       ):
        ret = do_svcs_action_detached(argv)
    else:
        try:
            ret = node.do_svcs_action(action, options)
        except ex.excError as exc:
            print(exc, file=sys.stderr)
            ret = 1
    return ret

def do_svc_create(node, svcnames, action, options, build_kwargs):
    """
    Handle service creation command.
    """
    ret = 0
    try:
        ret = node.install_service(svcnames, fpath=options.config,
                                   template=options.template)
    except Exception as exc:
        print(str(exc), file=sys.stderr)
        return 1

    if ret == 2:
        # install_service() reports it did nothing
        data = getattr(svcBuilder, action)(svcnames, options.resource,
                                           provision=options.provision)
    else:
        data = {"rid": [], "ret": 0}

    # if the user want to provision a resource defined via configuration
    # file edition, he will set --rid <rid> or --tag or --subset to point
    # the update command to it
    options.rid = ",".join(data.get("rid", []))

    # force a refresh of node.svcs
    try:
        node.rebuild_services(svcnames, build_kwargs["minimal"])
    except ex.excError as exc:
        print(exc, file=sys.stderr)
        ret = 1

    if len(node.svcs) == 1 and action == "create":
        node.svcs[0].setenv(options.env, options.interactive)
        # setenv changed the service config file
        # we need to rebuild again
        try:
            node.rebuild_services(svcnames, build_kwargs["minimal"])
        except ex.excError as exc:
            print(exc, file=sys.stderr)
            ret = 1

    if options.provision:
        if len(node.svcs) == 1 and action in ("create", "pull"):
            node.svcs[0].action("provision", options)

    if ret != 0:
        return ret

    return data["ret"]

def prepare_options(options):
    """
    Prepare and return the options Storage() as expected by the Svc::action
    and Node::do_svcs_action methods.
    """
    opts = Storage()
    for key, val in options.__dict__.items():
        opts[key.replace("parm_", "")] = val
    return opts

def split_env(arg):
    idx = arg.index("=")
    option = arg[:idx]
    value = arg[idx+1:]
    return option, value

def export_env_from_options(options):
    for arg in options.get("env", []):
        option, value = split_env(arg)
        option = option.upper()
        os.environ[option] = value

def _main(node, argv=None):
    """
    Build the service list, full or minimal depending on the requested action.
    Execute action-specific codepaths.
    """
    build_err = False
    svcnames = []
    ret = 0

    argv, docker_argv = get_docker_argv(argv)
    optparser = SvcmgrOptParser()
    options, action = optparser.parse_args(argv)
    options = prepare_options(options)
    export_env_from_options(options)
    options.docker_argv = docker_argv
    rcColor.use_color = options.color
    try:
        node.options.format = options.format
    except AttributeError:
        pass
    if os.environ.get("OSVC_SERVICE_LINK") is None and \
       action != "ls" and options.svcs is None and options.status is None:
        raise ex.excError("no service specified. set --service or --status.")
    if action != "create":
        expanded_svcs = node.svcs_selector(options.svcs)
        if options.svcs in (None, "*") and expanded_svcs == []:
            return
        options.svcs = expanded_svcs
    else:
        options.svcs = options.svcs.split(",")
    node.options.single_service = options.svcs is not None and \
                                  len(options.svcs) == 1

    build_kwargs = get_build_kwargs(optparser, options, action)

    if action != "create":
        try:
            node.build_services(**build_kwargs)
        except IOError as exc:
            if exc.errno == errno.EACCES:
                check_privs()
                return 1
            if len(str(exc)) > 0:
                print(exc, file=sys.stderr)
            build_err = True
        except ex.excError as exc:
            if len(str(exc)) > 0:
                print(exc, file=sys.stderr)
            build_err = True

    if node.svcs is not None and len(node.svcs) > 0:
        svcnames = [svc.svcname for svc in node.svcs]
    elif action == "create" and "svcnames" in build_kwargs:
        svcnames = build_kwargs["svcnames"]

    if len(svcnames) == 0:
        if action == "ls":
            return
        if not build_err:
            sys.stderr.write("No service specified. Try:\n"
                             " svcmgr -s <svcname>[,<svcname>]\n"
                             " svcmgr --status <status>[,<status>]\n"
                             " <svcname>\n")
        return 1

    if action == "create":
        return do_svc_create(node, svcnames, action, options, build_kwargs)

    node.set_rlimit()
    ret = do_svcs_action(node, options, action, argv=argv)

    try:
        import logging
        logging.shutdown()
    except:
        pass

    return ret

def main(argv=None):
    """
    Instanciate a Node object.
    Call the real deal making sure the node is finally freed.
    """
    ret = 0
    node_mod = ximport('node')
    try:
        node = node_mod.Node()
    except Exception as exc:
        print(exc, file=sys.stderr)
        return 1

    try:
        ret = _main(node, argv=argv)
    except ex.excError as exc:
        print(exc, file=sys.stderr)
        return 1
    except IOError as exc:
        if exc.errno == errno.EACCES:
            check_privs()
            return 1
        else:
            raise
    except KeyboardInterrupt:
        return 1
    finally:
        node.close()

    if ret is None:
        ret = 0

    return ret

if __name__ == "__main__":
    ret = main()
    sys.exit(ret)
   0707010005ca13000081b40000000000000000000000015ab217540000b4b8000000660000000000000000000000000000002c00000000root/usr/share/opensvc/lib/svcmgr_parser.py   """
svcmgr command line actions and options
"""
from rcGlobalEnv import Storage
from rcOptParser import OptParser
from optparse import Option

PROG = "svcmgr"

OPT = Storage({
    "account": Option(
        "--account", default=False,
        action="store_true", dest="account",
        help="If set the unavailabity period is accounted in the service "
             "availability ratio maintained by the collector."),
    "add": Option(
        "--add", default=None,
        action="store",
        help="A list member to add to the value pointed by :opt:``--param``. "
             "If :opt:``--index`` is set, insert the new element at the "
             "specified position in the list."),
    "attach": Option(
        "--attach", default=False,
        action="store_true", dest="attach",
        help="Attach the modulesets specified in a compliance run."),
    "author": Option(
        "--author", default=None,
        action="store", dest="author",
        help="The acker name to log when acknowledging action log errors"),
    "backlog": Option(
        "--backlog", default=None,
        action="store", dest="backlog",
        help="A size expression limiting the volume of data fetched "
             "from the log file tail. Default is 10k."),
    "begin": Option(
        "--begin", default=None,
        action="store", dest="begin",
        help="A begin date expressed as ``YYYY-MM-DD hh:mm`` limiting the "
             "timerange the action applies to."),
    "cluster": Option(
        "-c", "--cluster", default=False,
        action="store_true", dest="cluster",
        help="Execute the action on all cluster nodes. Aggregate the json "
             "outputs."),
    "color": Option(
        "--color", default="auto",
        action="store", dest="color",
        help="Colorize output. Possible values are:\n\n"
             "* auto: guess based on tty presence\n"
             "* always|yes: always colorize\n"
             "* never|no: never colorize"),
    "comment": Option(
        "--comment", default=None,
        action="store", dest="comment",
        help="A comment to log when acknowldging action log error entries."),
    "config": Option(
        "--config", default=None,
        action="store", dest="parm_config",
        help="The configuration file to use as template when creating or "
             "installing a service"),
    "cron": Option(
        "--cron", default=False,
        action="store_true", dest="cron",
        help="If set, the action is actually executed only if the scheduling"
             "constraints are satisfied."),
    "debug": Option(
        "--debug", default=False,
        action="store_true", dest="debug",
        help="Increase stream log verbosity up to the debug level."),
    "daemon": Option(
        "--daemon", default=False,
        action="store_true", dest="daemon",
        help="A flag inhibiting the daemonization. Set by the "
             "daemonization routine."),
    "disable_rollback": Option(
        "--disable-rollback", default=False,
        action="store_true", dest="disable_rollback",
        help="If set, don't try to rollback resources activated before a "
             "start action interrupts on error."),
    "discard": Option(
        "--discard", default=False,
        action="store_true", dest="discard",
        help="Discard the stashed, invalid, configuration file."),
    "downto": Option(
        "--downto", default=None,
        action="store", dest="upto",
        help="Stop the service down to the specified rid or driver group."),
    "dry_run": Option(
        "--dry-run", default=False,
        action="store_true", dest="dry_run",
        help="Show the action execution plan."),
    "duration": Option(
        "--duration", default=None,
        action="store", dest="duration",
        help="A duration expression like, ``1h10m``."),
    "end": Option(
        "--end", default=None,
        action="store", dest="end",
        help="A end date expressed as ``YYYY-MM-DD hh:mm`` limiting the "
             "timerange the action applies to."),
    "env": Option(
        "--env", default=[],
        action="append", dest="env",
        help="Export the uppercased variable in the os environment.\n\n"
             "With the create action only, set a env section parameter in "
             "the service configuration file. Multiple ``--env <key>=<val>`` "
             "can be specified. For all other actions."),
    "eval": Option(
        "--eval", default=False,
        action="store_true", dest="eval",
        help="If set with the :cmd:`svcmgr get` action, the printed value of "
             ":opt:`--param` is evaluated, scoped and dereferenced."),
    "follow": Option(
        "--follow", default=False,
        action="store_true", dest="follow",
        help="Follow the logs as they come. Use crtl-c to interrupt."),
    "force": Option(
        "-f", "--force", default=False,
        action="store_true", dest="force",
        help="Force action, ignore sanity checks."),
    "format": Option(
        "--format", default=None,
        action="store", dest="format",
        help="Specify a data formatter. Possible values are json, csv"
             " or table."),
    "help": Option(
        "-h", "--help", default=None,
        action="store_true", dest="parm_help",
        help="Show this help message and exit."),
    "hide_disabled": Option(
        "--hide-disabled", default=None,
        action="store_false", dest="show_disabled",
        help="Do not include the disabled resources. This option supersedes "
             "the :kw:`show_disabled` value in the service configuration."),
    "id": Option(
        "--id", default=0,
        action="store", dest="id", type="int",
        help="Specify an object id to act on"),
    "impersonate": Option(
        "--impersonate", default=None,
        action="store",
        help="Impersonate a peer node when evaluating keywords."),
    "index": Option(
        "--index", default=None,
        action="store", type="int",
        help="The position in the list pointed by --param where to add "
             "the new list element on a set action"),
    "interactive": Option(
        "-i", "--interactive", default=False,
        action="store_true", dest="interactive",
        help="Prompt the user for a choice instead of using defaults, "
             "or failing if no default is defined."),
    "kw": Option(
        "--kw", action="append", dest="kw",
        help="An expression like ``[<section>.]<keyword>[@<scope>][[<index>]]<op><value>`` where\n\n"
             "* <section> can be:\n\n"
             "  * a resource id\n"
             "  * a resource driver group name (fs, ip, ...). In this case, the set applies to all matching resources.\n"
             "* <op> can be:\n\n"
             "  * ``=``\n"
             "  * ``+=``\n"
             "  * ``-=``\n\n"
             "Multiple --kw can be set to apply multiple configuration change "
             "in a file with a single write.\n\n"
             "Examples:\n\n"
             "* app.start=false\n"
             "  Turn off app start for all app resources\n"
             "* app#1.start=true\n"
             "  Turn on app start for app#1\n"
             "* nodes+=node3\n"
             "  Append node3 to nodes\n"
             "* nodes[0]+=node3\n"
             "  Preprend node3 to nodes\n"),
    "like": Option(
        "--like", default="%",
        action="store", dest="like",
        help="A data filtering expression. ``%`` is the multi-character "
             "wildcard. ``_`` is the single-character wildcard. Leading and "
             "trailing ``%`` are automatically set."),
    "local": Option(
        "--local", default=False,
        action="store_true", dest="local",
        help="Execute the service action on the local service "
             "instances only, ignoring cluster-wide considerations."),
    "master": Option(
        "--master", default=False,
        action="store_true", dest="master",
        help="Limit the action scope to the master service resources."),
    "message": Option(
        "--message", default="",
        action="store", dest="message",
        help="The message to send to the collector for logging."),
    "module": Option(
        "--module", default="",
        action="store", dest="module",
        help="Specify the modules to limit the run to. The modules must be in already attached modulesets."),
    "moduleset": Option(
        "--moduleset", default="",
        action="store", dest="moduleset",
        help="Specify the modulesets to limit the action to. The special value ``all`` "
             "can be used in conjonction with detach."),
    "node": Option(
        "--node", default="",
        action="store", dest="node",
        help="The node to send a request to. If not specified the local node is targeted."),
    "nopager": Option(
        "--no-pager", default=False,
        action="store_true", dest="nopager",
        help="Do not display the command result in a pager."),
    "parallel": Option(
        "-p", "--parallel", default=False,
        action="store_true", dest="parallel",
        help="Start actions on specified services in parallel. :kw:`max_parallel` "
             "in node.conf limits the number of parallel running subprocesses."),
    "param": Option(
        "--param", default=None,
        action="store", dest="param",
        help="An expression like ``[<section>.]<keyword>`` where\n\n"
             "* <section> can be:\n\n"
             "  * a resource id\n"
             "  * a resource driver group name (fs, ip, ...). In this case, the set applies to all matching resources."),
    "provision": Option(
        "--provision", default=False,
        action="store_true", dest="provision",
        help="Provision the service resources after config file creation. "
             "Defaults to False."),
    "recover": Option(
        "--recover", default=False,
        action="store_true", dest="recover",
        help="Recover the stashed erroneous configuration file "
             "in a :cmd:`svcmgr edit config` command"),
    "refresh": Option(
        "-r", "--refresh", default=False,
        action="store_true", dest="refresh",
        help="Drop status caches and re-evaluate before printing."),
    "remove": Option(
        "--remove", default=None,
        action="store",
        help="A list member to drop from the value pointed by :kw:`--param`."),
    "resource": Option(
        "--resource", default=[],
        action="append",
        help="A resource definition in json dictionary format fed to create "
             "or update. The ``type`` key point the driver group name, and "
             "the ``rtype`` key the driver name (translated to type in the "
             "configuration file section)."),
    "rid": Option(
        "--rid", default=None,
        action="store", dest="parm_rid",
        help="A resource specifier expression like ``<spec>[,<spec>]``, where ``<spec>`` can be:\n\n"
             "* A resource id\n"
             "* A driver group name (app, fs, disk, ...)\n\n"
             "Examples:\n\n"
             "* ``app``\n"
             "  all app resources\n"
             "* ``container#1,ip#1``\n"
             "  only container#1 and ip#1\n"
        ),
    "ruleset": Option(
        "--ruleset", default="",
        action="store", dest="ruleset",
        help="Specify the rulesets to limit the action to. The special value ``all`` "
             "can be used in conjonction with detach."),
    "ruleset_date": Option(
        "--ruleset-date", default="",
        action="store", dest="ruleset_date",
        help="Use an historical ruleset, specified by its date."),
    "service": Option(
        "-s", "--service", default=None,
        action="store", dest="parm_svcs",
        help="A service selector expression ``[!]<expr>[<sep>[!]<expr>]`` where:\n\n"
             "- ``!`` is the expression negation operator\n\n"
             "- ``<sep>`` can be:\n\n"
             "  - ``,`` OR expressions\n\n"
             "  - ``+`` AND expressions\n\n"
             "- ``<expr>`` can be:\n\n"
             "  - a shell glob on service names\n\n"
             "  - ``<param><op><value>`` where:\n\n"
             "    - ``<param>`` can be:\n\n"
             "      - ``<rid>:``\n\n"
             "      - ``<group>:``\n\n"
             "      - ``<rid>.<key>``\n\n"
             "      - ``<group>.<key>``\n\n"
             "    - ``<op>`` can be:\n\n"
             "      - ``<``  ``>``  ``<=``  ``>=``  ``=``\n\n"
             "      - ``~`` with regexp value\n\n"
             "Examples:\n\n"
             "- ``*dns,ha*+app.timeout>1``\n\n"
             "- ``ip:+task:``\n\n"
             "- ``!*excluded``\n\n"
             "Note:\n\n"
             "- ``!`` usage requires single quoting the expression to prevent "
             "shell history expansion"),
    "show_disabled": Option(
        "--show-disabled", default=None,
        action="store_true", dest="show_disabled",
        help="Include the disabled resources. This option supersedes "
             "the :kw:`show_disabled` value in the service configuration."),
    "slave": Option(
        "--slave", default=None, action="store", dest="slave",
        help="Limit the action to the service resources in the specified, comma-"
             "separated, slaves."),
    "slaves": Option(
        "--slaves", default=False,
        action="store_true", dest="slaves",
        help="Limit the action scope to service resources in all slaves."),
    "status": Option(
        "--status", default=None,
        action="store", dest="parm_status",
        help="Operate only on service with a local instance in the specified availability status "
             "(up, down, warn, ...)."),
    "subsets": Option(
        "--subsets", default=None,
        action="store", dest="parm_subsets",
        help="Limit the action to the resources in the specified, comma-separated, list of subsets."),
    "tag": Option(
        "--tag", default=None,
        action="store", dest="tag",
        help="The tag name, as shown by :cmd:`svcmgr collector list tags`."),
    "tags": Option(
        "--tags", default=None,
        action="store", dest="parm_tags",
        help="A comma-separated list of resource tags to limit "
             "action to. The ``+`` separator can be used to impose "
             "multiple tag conditions. For example, ``tag1+tag2,tag3`` "
             "limits the action to resources with both tag1 and"
             " tag2, or tag3."),
    "template": Option(
        "--template", default=None,
        action="store", dest="parm_template",
        help="The configuration file template name or id, "
             "served by the collector, to use when creating or "
             "installing a service."),
    "time": Option(
        "--time", default="300",
        action="store", dest="time",
        help="A duration expression like ``1m5s``. The maximum wait time for an "
             "async action to finish. Default is 300 seconds."),
    "to": Option(
        "--to", default=None,
        action="store", dest="parm_destination_node",
        help="The remote node to start or migrate the service to. Or the "
             "target number of instance to scale to."),
    "unprovision": Option(
        "--unprovision", default=False,
        action="store_true", dest="unprovision",
        help="Unprovision the service resources before config files file deletion. "
             "Defaults to False."),
    "upto": Option(
        "--upto", default=None,
        action="store", dest="upto",
        help="Start the service up to the specified rid or driver group."),
    "value": Option(
        "--value", default=None,
        action="store", dest="value",
        help="The value to set for the keyword pointed by :opt:`--param`"),
    "verbose": Option(
        "--verbose", default=False,
        action="store_true", dest="verbose",
        help="Include more information to some print commands output. "
             "For example, add the ``next run`` column in the output of "
             ":cmd:`svcmgr print schedule`."),
    "wait": Option(
        "--wait", default=False,
        action="store_true", dest="wait",
        help="Wait for asynchronous action termination."),
    "waitlock": Option(
        "--waitlock", default="-1",
        action="store", dest="parm_waitlock",
        help="A duration expression like ``5s``. The maximum wait time when acquiring "
             "the service action lock."),
})

SVC_SELECT_OPTS = [
    OPT.service,
    OPT.status,
]

GLOBAL_OPTS = SVC_SELECT_OPTS + [
    OPT.cluster,
    OPT.color,
    OPT.daemon,
    OPT.debug,
    OPT.env,
    OPT.parallel,
    OPT.waitlock,
    OPT.help,
]

ACTION_OPTS = [
    OPT.dry_run,
    OPT.force,
    OPT.local,
    OPT.master,
    OPT.node,
    OPT.rid,
    OPT.slave,
    OPT.slaves,
    OPT.subsets,
    OPT.tags,
]

ASYNC_ACTION_OPTS = [
    OPT.time,
    OPT.wait,
]

START_ACTION_OPTS = [
    OPT.disable_rollback,
]

DAEMON_OPTS = [
    OPT.node,
    OPT.local,
]

ACTIONS = {
    "Service actions": {
        "abort": {
            "msg": "Abort the action asynchronously done by the cluster daemons.",
        },
        "clear": {
            "msg": "Clear the monitor status of the service on the node pointed "
                   "by --node. If --node is not specified, all nodes are "
                   "cleared. This command can be used to reactivate service "
                   "orchestration blocked by a failed status like ``start failed``.",
            "options": DAEMON_OPTS,
        },
        "dns_update": {
            "msg": "Update the collector dns records for the service. The "
                   "managed dns record is <svcname>.<app>.<collector "
                   "domain>``.",
            "options": ACTION_OPTS,
        },
        "shutdown": {
            "msg": "Stop a service, including its standby resources. The log "
                   "shipping to the collector is synchronous.",
            "options": ACTION_OPTS + ASYNC_ACTION_OPTS,
        },
        "start": {
            "msg": "Start a service. The started instances depend on the "
                   "service placement policy, so the local instance may not "
                   "start. A failover service is considered started when one "
                   "instance is started. A flex service is considered started "
                   "when ``<flex_min_nodes>`` instances are started.",
            "options": ACTION_OPTS + START_ACTION_OPTS + ASYNC_ACTION_OPTS + [
                OPT.upto,
            ],
        },
        "startstandby": {
            "msg": "Start local service instance resources flagged standby.",
            "options": ACTION_OPTS + START_ACTION_OPTS,
        },
        "stop": {
            "msg": "Stop all service instances. The standby resources "
                   "are not stopped, unless :opt:`--force` is specified.",
            "options": ACTION_OPTS + ASYNC_ACTION_OPTS + [
                OPT.downto,
            ],
        },
        "provision": {
            "msg": "Provision the service. Leave the service in frozen, stdby up state.",
            "options": ASYNC_ACTION_OPTS + ACTION_OPTS + START_ACTION_OPTS,
        },
        "unprovision": {
            "msg": "Shutdown and unprovision all service instances. Beware, data will be "
                   "lost upon fs and disk unprovisioning.",
            "options": ASYNC_ACTION_OPTS + ACTION_OPTS,
        },
        "disable": {
            "msg": "Disable resources specified by :opt:`--rid` in services specified by "
                   ":opt:`--service`. Specifying no resource disables the whole service.",
            "options": [
                OPT.rid,
                OPT.tags,
                OPT.subsets,
            ],
        },
        "enable": {
            "msg": "Enable resources specified by :opt:`--rid` in services specified by "
                   ":opt:`--service`. Specifying no resource enables the whole service.",
            "options": [
                OPT.rid,
                OPT.tags,
                OPT.subsets,
            ],
        },
        "ls": {
            "msg": "List the service names with a local instance. Most useful to test "
                   "a service selector expression before running an action.",
        },
        "status": {
            "msg": "Return the local service instance overall status code.",
            "options": [
                OPT.cron,
                OPT.refresh,
            ],
        },
        "print_status": {
            "msg": "Display the service status, with a detailed view of the local "
                   "instance.\n\n"
                   "Resources Flags:\n"
                   "* ``R``  Running\n"
                   "* ``M``  Monitored\n"
                   "* ``D``  Disabled\n"
                   "* ``O``  Optional\n"
                   "* ``E``  Encap\n"
                   "* ``P``  Provisioned\n"
                   "* ``S``  Standby\n"
                   "",
            "options": [
                OPT.format,
                OPT.hide_disabled,
                OPT.refresh,
                OPT.show_disabled,
            ],
        },
        "print_resource_status": {
            "msg": "Display a specific service resource status, pointed by"
                   " --rid",
            "options": [
                OPT.format,
                OPT.refresh,
                OPT.rid,
            ],
        },
        "print_config_mtime": {
            "msg": "Display the service local configuration file modification time",
        },
        "set_provisioned": {
            "msg": "Set the resources as provisioned.",
            "options": ACTION_OPTS,
        },
        "set_unprovisioned": {
            "msg": "Set the resources as unprovisioned.",
            "options": ACTION_OPTS,
        },
        "freeze": {
            "msg": "Block orchestration on the service.",
            "options": ASYNC_ACTION_OPTS + [
                OPT.node,
                OPT.local,
                OPT.master,
                OPT.slave,
                OPT.slaves,
            ],
        },
        "thaw": {
            "msg": "Unblock orchestration on the service.",
            "options": ASYNC_ACTION_OPTS + [
                OPT.node,
                OPT.local,
                OPT.master,
                OPT.slave,
                OPT.slaves,
            ],
        },
        "toc": {
            "msg": "Trigger the service instance pre_monitor_action script and monitor_action method. Beware, this might crash or reboot the local node.",
            "options": ACTION_OPTS,
        },
        "frozen": {
            "msg": "Report on the current blocking of orchestration on the service.",
        },
        "run": {
            "msg": "Run all tasks, or tasks specified by --rid --tags and "
                   "--subset, disregarding their schedule.",
            "options": ACTION_OPTS + [
                OPT.cron,
            ],
        },
        "presync": {
            "msg": "Execute the presync method of the resource driver for each local service instance resource. These methods usually update var files needing replication on other nodes.",
            "options": ACTION_OPTS,
        },
        "postsync": {
            "msg": "Execute the postsync method of the resource driver for each local service instance resource. These methods usually take appropriate action based on var files received from the primary node.",
            "options": ACTION_OPTS,
        },
        "prstatus": {
            "msg": "Report the status of scsi3 persistent reservations on scsi disks held by the local "
                   "service instance.",
        },
        "restart": {
            "msg": "Chain a local service instance stop and start",
            "options": ACTION_OPTS + START_ACTION_OPTS,
        },
        "resync": {
            "msg": "Chain a local service instance  stop, sync_resync and start",
            "options": ACTION_OPTS + START_ACTION_OPTS,
        },
        "sync_nodes": {
            "msg": "Run the synchronization method of each local service instance sync resource, targetting the peer nodes.",
            "options": ACTION_OPTS,
        },
        "sync_drp": {
            "msg": "Run the synchronization method of each local service instance sync resource, targetting the drp nodes.",
            "options": ACTION_OPTS,
        },
        "sync_quiesce": {
            "msg": "Pause replication of sync.netapp and sync.symsrdf resources.",
            "options": ACTION_OPTS,
        },
        "sync_break": {
            "msg": "Break the disk replication of sync.dcsckpt, sync.hp3par, sync.ibmdssnap, sync.netapp, sync.symclone, sync.symsrdf resources.",
            "options": ACTION_OPTS,
        },
        "sync_split": {
            "msg": "Split the disk replication of sync.symsrdf resources.",
            "options": ACTION_OPTS,
        },
        "sync_establish": {
            "msg": "Establish disk replication of sync.symsrdf resources.",
            "options": ACTION_OPTS,
        },
        "sync_resync": {
            "msg": "Like :cmd:`sync update`, but not triggered by the scheduler "
                   "(thus adapted for clone/snap operations).",
            "options": ACTION_OPTS,
        },
        "sync_full": {
            "msg": "Trigger a full copy of the volume to its target.",
            "options": ACTION_OPTS,
        },
        "sync_restore": {
            "msg": "Trigger a restore of the sync resources data to their "
                   "target path (DANGEROUS: make sure you understand before "
                   "running this action).",
            "options": ACTION_OPTS,
        },
        "sync_update": {
            "msg": "Trigger a one-time resync of the volume to its target.",
            "options": ACTION_OPTS,
        },
        "sync_resume": {
            "msg": "Re-establish a broken storage hardware-assisted "
                   "synchronization.",
            "options": ACTION_OPTS,
        },
        "sync_revert": {
            "msg": "Revert to the pre-failover data (looses current data).",
            "options": ACTION_OPTS,
        },
        "sync_verify": {
            "msg": "Trigger a one-time checksum-based verify of the volume "
                   "and its target.",
            "options": ACTION_OPTS,
        },
        "sync_all": {
            "msg": "Chain sync nodes, sync drp and sync update.",
            "options": ACTION_OPTS + [
                OPT.cron,
            ],
        },
        "push_config": {
            "msg": "Push service configuration to the collector.",
            "options": [
                OPT.cron,
            ],
        },
        "push_encap_config": {
            "msg": "Push service configuration to the containers hosting an encapsulated service.",
            "options": [
                OPT.cron,
            ],
        },
        "pull": {
            "msg": "Pull a service configuration from the collector, overwritting the currently installed one.",
            "options": [
                OPT.disable_rollback,
                OPT.provision,
            ],
        },
        "push_resinfo": {
            "msg": "Push the local service instance resources and application launchers info "
                   "key/value pairs the collector.",
            "options": [
                OPT.cron,
            ],
        },
        "print_base_devs": {
            "msg": "Print the list of base devices the local service instance or the "
                   "specified resources are layered on.",
            "options": [
                OPT.format,
                OPT.rid,
                OPT.tags,
                OPT.subsets,
            ],
        },
        "print_exposed_devs": {
            "msg": "Print the list of devices the local service instance or the specified "
                   "resources expose.",
            "options": [
                OPT.format,
                OPT.rid,
                OPT.tags,
                OPT.subsets,
            ],
        },
        "print_sub_devs": {
            "msg": "Print the list of devices the local service instance or the specified "
                   "resources are layered on.",
            "options": [
                OPT.format,
                OPT.rid,
                OPT.tags,
                OPT.subsets,
            ],
        },
        "print_devs": {
            "msg": "Aggregate the information of :cmd:`print base devs`, :cmd:`print sub devs` and :cmd:`print exposed devs`.",
            "options": [
                OPT.format,
                OPT.rid,
                OPT.tags,
                OPT.subsets,
            ],
        },
        "scale": {
            "msg": "Create-provision or delete-unprovision instances to meet "
                   "service scale target.",
            "options": DAEMON_OPTS + [
                OPT.to
            ],
        },
        "switch": {
            "msg": "Stop the running failover service instance and start the "
                   "instance on the peer node specified by :opt:`--to "
                   "<nodename>`.",
            "options": ACTION_OPTS + START_ACTION_OPTS + ASYNC_ACTION_OPTS + [
                OPT.to,
            ],
        },
        "takeover": {
            "msg": "Stop the service on its current node and start on the "
                   "local node.",
            "options": ACTION_OPTS + START_ACTION_OPTS + ASYNC_ACTION_OPTS
        },
        "giveback": {
            "msg": "Stop the service on its current node and start on the "
                   "node chosen by the placement policy.",
            "options": ACTION_OPTS + START_ACTION_OPTS + ASYNC_ACTION_OPTS
        },
        "migrate": {
            "msg": "Live migrate the service to the remote node. "
                   "--to <node> specify the remote node to migrate the "
                   "service to.",
            "options": ACTION_OPTS + START_ACTION_OPTS + ASYNC_ACTION_OPTS + [
                OPT.to,
            ],
        },
        "resource_monitor": {
            "msg": "Refresh the monitored resource status. This action is "
                   "scheduleable, usually every minute.",
            "options": ACTION_OPTS + [
                OPT.cron,
            ],
        },
        "docker": {
            "msg": "Wrap the docker client command, setting automatically "
                   "the socket parameter to join the service-private docker "
                   "daemon. The {as_service}, {images} and {instances} words "
                   "in the wrapped command are replaced by, respectively, "
                   "the registry login username/password/email parameters to "
                   "log as a service using <svcname>@<nodename> as the "
                   "username and the node uuid as password (which is what "
                   "is expected when the opensvc collector is used as the "
                   "JWT manager for the registry), the set of docker "
                   "instance names and images for container resources "
                   "passing the --tags, --rid and --subsets filters. This is "
                   "useful to remove all instances of a service or all "
                   "instances of resources with a tag like 'frontend'. Note "
                   "the opensvc filters must be positioned before the docker "
                   "command in the arguments list.",
        },
        "print_schedule": {
            "msg": "Print the service tasks schedule.",
            "options": [
                OPT.format,
                OPT.verbose,
            ],
        },
        "pg_freeze": {
            "msg": "Freeze the tasks of a process group.",
            "options": ACTION_OPTS,
        },
        "pg_thaw": {
            "msg": "Thaw the tasks of a process group.",
            "options": ACTION_OPTS,
        },
        "pg_kill": {
            "msg": "Kill the tasks of a process group.",
            "options": ACTION_OPTS,
        },
        "logs": {
            "msg": "Display the service logs. All service instances logs are aggregated.",
            "options": [
                OPT.backlog,
                OPT.follow,
                OPT.local,
                OPT.node,
                OPT.nopager,
            ]
        },
    },
    "Service configuration": {
        "print_resinfo": {
            "msg": "Display the service resource and env section key/val pairs "
                   "pushed to the collector.",
            "options": [
                OPT.format,
            ],
        },
        "print_config": {
            "msg": "Display the service current configuration.",
            "options": [
                OPT.format,
                OPT.eval,
                OPT.impersonate,
            ],
        },
        "edit_config": {
            "msg": "Edit the service configuration. The new configuration file is actually installed only if it passes validation, so this action is recommended over direct edition.",
            "options": [
                OPT.discard,
                OPT.recover,
            ],
        },
        "validate_config": {
            "msg": "Check the section names and keywords are valid.",
        },
        "create": {
            "msg": "Create a new service.",
            "options": ACTION_OPTS + [
                OPT.config,
                OPT.disable_rollback,
                OPT.interactive,
                OPT.provision,
                OPT.resource,
                OPT.template,
            ],
        },
        "update": {
            "msg": "Update definitions in an existing service configuration "
                   "file.",
            "options": ACTION_OPTS + [
                OPT.disable_rollback,
                OPT.provision,
                OPT.resource,
            ],
        },
        "delete": {
            "msg": "Delete a service, or only the resources specified by :opt:`--rid` on the local service instance.",
            "options": ASYNC_ACTION_OPTS + ACTION_OPTS + [
                OPT.unprovision,
            ],
        },
        "set": {
            "msg": "Set a service configuration parameter",
            "options": ACTION_OPTS + [
                OPT.kw,
                OPT.add,
                OPT.index,
                OPT.param,
                OPT.remove,
                OPT.value,
            ],
        },
        "get": {
            "msg": "Get the raw or evaluated value of a service "
                   "configuration keyword.",
            "options": ACTION_OPTS + [
                OPT.eval,
                OPT.impersonate,
                OPT.param,
                OPT.kw,
            ],
        },
        "unset": {
            "msg": "Unset a node configuration keyword.",
            "options": ACTION_OPTS + [
                OPT.kw,
                OPT.param,
            ],
        },
    },
    "Compliance": {
        "compliance_auto": {
            "msg": "Run compliance checks or fixes, depending on the autofix "
                   "module property values.",
            "options": [
                OPT.attach,
                OPT.cron,
                OPT.force,
                OPT.module,
                OPT.moduleset,
                OPT.ruleset_date,
            ],
        },
        "compliance_check": {
            "msg": "Run compliance checks.",
            "options": [
                OPT.attach,
                OPT.force,
                OPT.module,
                OPT.moduleset,
                OPT.ruleset_date,
            ],
        },
        "compliance_fix": {
            "msg": "Run compliance fixes.",
            "options": [
                OPT.attach,
                OPT.force,
                OPT.module,
                OPT.moduleset,
                OPT.ruleset_date,
            ],
        },
        "compliance_fixable": {
            "msg": "Verify compliance fixes prerequisites.",
            "options": [
                OPT.attach,
                OPT.force,
                OPT.module,
                OPT.moduleset,
                OPT.ruleset_date,
            ],
        },
        "compliance_env": {
            "msg": "Show the environment variables set during a compliance module run.",
            "options": [
                OPT.module,
                OPT.moduleset,
            ],
        },
        "compliance_show_status": {
            "msg": "Show compliance modules status.",
        },
        "compliance_show_moduleset": {
            "msg": "Show compliance rules applying to this service.",
        },
        "compliance_list_moduleset": {
            "msg": "List available compliance modulesets. Setting :opt:`--moduleset f%` "
                   "limits the resultset to modulesets matching the ``f%`` pattern.",
            'options': [
                OPT.moduleset,
            ],
        },
        "compliance_list_ruleset": {
            "msg": "List available compliance rulesets. Setting :opt:`--ruleset f%` limits "
                   "the scope to rulesets matching the ``f%`` pattern.",
            'options': [
                OPT.ruleset,
            ],
        },
        "compliance_show_ruleset": {
            "msg": "Show compliance rules applying to this service.",
        },
        "compliance_attach": {
            "msg": "Attach rulesets specified by :opt:`--ruleset` and modulesets "
                   "specified by :opt:`--moduleset` to this service. Attached modulesets "
                   "are scheduled for check or autofix.",
            "options": [
                OPT.moduleset,
                OPT.ruleset,
            ],
        },
        "compliance_detach": {
            "msg": "Detach rulesets specified by :opt:`--ruleset` and modulesets "
                   "specified by :opt:`--moduleset` from this service. Detached "
                   "modulesets are no longer scheduled for check and autofix.",
            "options": [
                OPT.moduleset,
                OPT.ruleset,
            ],
        },
    },
    "Collector management": {
        "collector_ack_unavailability": {
            "msg": "Acknowledge an unavailability period. The period is "
                   "specified by :opt:`--begin` :opt:`--end` or :opt:`--begin` :opt:`--duration`. "
                   ":opt:`--begin` defaults to now.",
            "options": [
                OPT.author,
                OPT.account,
                OPT.begin,
                OPT.end,
                OPT.comment,
                OPT.duration,
            ],
        },
        "collector_list_unavailability_ack": {
            "msg": "List acknowledged periods for the service.",
            "options": [
                OPT.author,
                OPT.begin,
                OPT.end,
                OPT.comment,
            ],
        },
        "collector_list_actions": {
            "msg": "List actions on the service, Whatever the node, during "
                   "the period specified by --begin/--end. --end defaults to "
                   "now. --begin defaults to 7 days ago.",
            "options": [
                OPT.begin,
                OPT.end,
                OPT.format,
            ],
        },
        "collector_ack_action": {
            "msg": "Acknowledge an action error on the service. An "
                   "acknowlegment can be completed by --author (defaults "
                   "to root@nodename) and --comment.",
            "options": [
                OPT.author,
                OPT.comment,
            ],
        },
        "collector_show_actions": {
            "msg": "Show actions detailed log. A single action is specified "
                   "by --id. A range is specified by --begin/--end dates. "
                   "--end defaults to now. --begin defaults to 7 days ago.",
            "options": [
                OPT.begin,
                OPT.id,
                OPT.end,
                OPT.format,
            ],
        },
        "collector_checks": {
            "msg": "Display service checks.",
            "options": [
                OPT.format,
            ],
        },
        "collector_disks": {
            "msg": "Display service disks.",
            "options": [
                OPT.format,
            ],
        },
        "collector_log": {
            "msg": "Log a message in the collector service log.",
            "options": [
                OPT.message,
            ],
        },
        "collector_alerts": {
            "msg": "Display service alerts.",
            "options": [
                OPT.format,
            ],
        },
        "collector_events": {
            "msg": "Display the service events during the period specified by "
                   "--begin/--end. --end defaults to now. --begin defaults "
                   "to 7 days ago.",
            "options": [
                OPT.begin,
                OPT.end,
                OPT.format,
            ],
        },
        "collector_asset": {
            "msg": "Display asset information known to the collector.",
            "options": [
                OPT.format,
            ],
        },
        "collector_networks": {
            "msg": "Display network information known to the collector for "
                   "each service ip.",
            "options": [
                OPT.format,
            ],
        },
        "collector_tag": {
            "msg": "Set a service tag (pointed by --tag).",
            "options": [
                OPT.tag,
            ],
        },
        "collector_untag": {
            "msg": "Unset a service tag (pointed by --tag).",
            "options": [
                OPT.tag,
            ],
        },
        "collector_show_tags": {
            "msg": "List all service tags.",
            "options": [
                OPT.format,
            ],
        },
        "collector_list_tags": {
            "msg": "List all available tags. Use :opt:`--like` to filter the output.",
            "options": [
                OPT.format,
                OPT.like,
            ],
        },
        "collector_create_tag": {
            "msg": "Create a new tag.",
            "options": [
                OPT.tag,
            ],
        },
    },
}

DEPRECATED_ACTIONS = [
    "collector_json_alerts",
    "collector_json_asset",
    "collector_json_checks",
    "collector_json_disks",
    "collector_json_events",
    "collector_json_list_actions",
    "collector_json_list_unavailability_ack",
    "collector_json_networks",
    "collector_json_show_actions",
    "collector_json_status",
    "push_appinfo",
    "json_config",
    "json_devs",
    "json_sub_devs",
    "json_base_devs",
    "json_env",
    "json_schedule",
    "json_status",
    "startapp",
    "startcontainer",
    "startdisk",
    "startfs",
    "startip",
    "startshare",
    "stopapp",
    "stopcontainer",
    "stopdisk",
    "stopfs",
    "stopip",
    "stopshare",
    "syncall",
    "syncbreak",
    "syncestablish",
    "syncnodes",
    "syncdrp",
    "syncfullsync",
    "syncquiesce",
    "syncresync",
    "syncsplit",
    "syncupdate",
    "syncresume",
    "syncrevert",
    "syncswap",
    "syncverify",
]

ACTIONS_TRANSLATIONS = {
    "push_env": "push_config",
    "push": "push_config",
    "json_env": "json_config",
    "startapp": {"action": "start", "mangle": lambda x: add_rid(x, ["app"])},
    "startip": {"action": "start", "mangle": lambda x: add_rid(x, ["ip"])},
    "startfs": {"action": "start", "mangle": lambda x: add_rid(x, ["disk", "fs"])},
    "startdisk": {"action": "start", "mangle": lambda x: add_rid(x, ["disk"])},
    "startshare": {"action": "start", "mangle": lambda x: add_rid(x, ["share"])},
    "startcontainer": {"action": "start", "mangle": lambda x: add_rid(x, ["container"])},
    "stopapp": {"action": "stop", "mangle": lambda x: add_rid(x, ["app"])},
    "stopip": {"action": "stop", "mangle": lambda x: add_rid(x, ["ip"])},
    "stopfs": {"action": "stop", "mangle": lambda x: add_rid(x, ["disk", "fs"])},
    "stopdisk": {"action": "stop", "mangle": lambda x: add_rid(x, ["disk"])},
    "stopshare": {"action": "stop", "mangle": lambda x: add_rid(x, ["share"])},
    "stopcontainer": {"action": "stop", "mangle": lambda x: add_rid(x, ["container"])},
    "prstart": {"action": "start", "mangle": lambda x: add_rid(x, ["disk.scsireserv"])},
    "prstop": {"action": "stop", "mangle": lambda x: add_rid(x, ["disk.scsireserv"])},
    "syncall": "sync_all",
    "syncbreak": "sync_break",
    "syncdrp": "sync_drp",
    "syncestablish": "sync_establish",
    "syncfullsync": "sync_full",
    "syncnodes": "sync_nodes",
    "syncquiesce": "sync_quiesce",
    "syncrestore": "sync_restore",
    "syncresume": "sync_resume",
    "syncresync": "sync_resync",
    "syncrevert": "sync_revert",
    "syncsplit": "sync_split",
    "syncswap": "sync_swap",
    "syncupdate": "sync_update",
    "syncverify": "sync_verify",
    "unfreeze": "thaw",
}

def add_rid(options, new_rids):
    if options.parm_rid is None:
        options.parm_rid = ",".join(new_rids)
        return options
    # discard incompatible rids
    rids = [rid for rid in options.parm_rid.split(",") if \
            rid.split('#')[0] in new_rids]
    if len(rids) == 0:
        options.parm_rid = "impossible"
        return options
    options.parm_rid = ",".join(rids)
    return options

class SvcmgrOptParser(OptParser):
    """
    The svcmgr-specific options parser class
    """
    def __init__(self, args=None, colorize=True, width=None, formatter=None,
                 indent=6):
        OptParser.__init__(self, args=args, prog=PROG, options=OPT,
                           actions=ACTIONS,
                           deprecated_actions=DEPRECATED_ACTIONS,
                           actions_translations=ACTIONS_TRANSLATIONS,
                           global_options=GLOBAL_OPTS,
                           svc_select_options=SVC_SELECT_OPTS,
                           colorize=colorize, width=width,
                           formatter=formatter, indent=indent)

0707010005ca14000081b40000000000000000000000015ab2175400000f14000000660000000000000000000000000000002500000000root/usr/share/opensvc/lib/svcmon.py  from __future__ import print_function
import sys
import optparse
import time
import datetime

#
# add project lib to path
#
prog = "svcmon"

import rcExceptions as ex
import rcColor
from node import Node

try:
    from version import version
except:
    version = "dev"

__ver = prog + " version " + version
__usage = prog + \
    " [ OPTIONS ]\n" \
    "\n" \
    "Flags:\n" \
    "  O  up\n" \
    "  o  stdby up\n" \
    "  X  down\n" \
    "  x  stdby down\n" \
    "  !  warn\n" \
    "  P  unprovisioned\n" \
    "  *  frozen\n" \
    "  ^  leader node or service placement non-optimal"
parser = optparse.OptionParser(version=__ver, usage=__usage)
parser.add_option("--color", default="auto",
                  action="store", dest="color",
                  help="colorize output. possible values are : auto=guess based "
                       "on tty presence, always|yes=always colorize, never|no="
                       "never colorize")
parser.add_option("--node", default="", action="store", dest="node",
                  help="The node to send a request to. If not specified the "
                       "local node is targeted."),
parser.add_option("-w", "--watch", default=False,
                  action="store_true", dest="watch",
                  help="refresh the information every --interval.")
parser.add_option("-i", "--interval", default=2, action="store",
                  dest="interval", type="int",
                  help="with --watch, set the refresh interval.")
parser.add_option(
    "-s", "--service", default=None,
    action="store", dest="parm_svcs",
    help="A service selector expression ``[!]<expr>[<sep>[!]<expr>]`` where:\n\n"
         "- ``!`` is the expression negation operator\n\n"
         "- ``<sep>`` can be:\n\n"
         "  - ``,`` OR expressions\n\n"
         "  - ``+`` AND expressions\n\n"
         "- ``<expr>`` can be:\n\n"
         "  - a shell glob on service names\n\n"
         "  - ``<param><op><value>`` where:\n\n"
         "    - ``<param>`` can be:\n\n"
         "      - ``<rid>:``\n\n"
         "      - ``<group>:``\n\n"
         "      - ``<rid>.<key>``\n\n"
         "      - ``<group>.<key>``\n\n"
         "    - ``<op>`` can be:\n\n"
         "      - ``<``  ``>``  ``<=``  ``>=``  ``=``\n\n"
         "      - ``~`` with regexp value\n\n"
         "Examples:\n\n"
         "- ``*dns,ha*+app.timeout>1``\n\n"
         "- ``ip:+task:``\n\n"
         "- ``!*excluded``\n\n"
         "Note:\n\n"
         "- ``!`` usage requires single quoting the expression to prevent "
         "shell history expansion")

def _main(node, argv=None):
    (options, args) = parser.parse_args(argv)
    node.check_privs(argv)
    rcColor.use_color = options.color

    while True:
        if options.parm_svcs:
            expanded_svcs = node.svcs_selector(options.parm_svcs)
        else:
            expanded_svcs = None

        node.options.update({
            "color": options.color,
        })
        if options.watch:
            preamble = "\033[H\033[J" + datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S\n")
        else:
            preamble = ""
        node.daemon_status(svcnames=expanded_svcs, preamble=preamble, node=options.node)
        if not options.watch:
            break
        time.sleep(options.interval)

def main(argv=None):
    if argv is None:
        argv = sys.argv
    else:
        argv.insert(0, __file__)

    try:
        node = Node()
    except Exception as exc:
        print(exc, file=sys.stderr)
        return 1

    try:
        _main(node, argv)
        return 0
    except ex.excError as e:
        print(e, file=sys.stderr)
        return 1
    except KeyboardInterrupt:
        sys.stderr.write("Keybord Interrupt\n")
        return 1
    finally:
        node.close()

    return 0

if __name__ == "__main__":
    ret = main()
    sys.exit(ret)

0707010005ca15000081b40000000000000000000000015ab21754000067a6000000660000000000000000000000000000002700000000root/usr/share/opensvc/lib/tabulate.py    # -*- coding: utf-8 -*-

"""Pretty-print tabular data."""

from __future__ import print_function
from __future__ import unicode_literals
from collections import namedtuple
from platform import python_version_tuple
import re


if python_version_tuple()[0] < "3":
    from itertools import izip_longest
    _none_type = type(None)
    _int_type = int
    _float_type = float
    _text_type = unicode
    _binary_type = str
else:
    from itertools import zip_longest as izip_longest
    from functools import reduce
    _none_type = type(None)
    _int_type = int
    _float_type = float
    _text_type = str
    _binary_type = bytes

__all__ = ["tabulate"]
__version__ = "0.6"

def __text_type(s):
    try:
       return _text_type(s, errors="ignore")
    except:
       return s

Line = namedtuple("Line", ["begin", "hline", "sep", "end"])


DataRow = namedtuple("DataRow", ["begin", "sep", "end"])


TableFormat = namedtuple("TableFormat", ["lineabove", "linebelowheader",
                                         "linebetweenrows", "linebelow",
                                         "headerrow", "datarow",
                                         "padding", "usecolons", "usehtmlattrs",
                                         "with_header_hide",
                                         "without_header_hide"])


_format_defaults = {"padding": 0,
                    "usecolons": False,
                    "usehtmlattrs": False,
                    "with_header_hide": [],
                    "without_header_hide": []}


_table_formats = {"simple":
                  TableFormat(lineabove=None,
                              linebelowheader=Line("", "-", "  ", ""),
                              linebetweenrows=None,
                              linebelow=Line("", "-", "  ", ""),
                              headerrow=DataRow("", "  ", ""),
                              datarow=DataRow("", "  ", ""),
                              padding=0,
                              usecolons=False,
                              usehtmlattrs=False,
                              with_header_hide=["linebelow"],
                              without_header_hide=[]),
                  "plain":
                  TableFormat(lineabove=None,
                              linebelowheader=None,
                              linebetweenrows=Line("+", "-", "+", "+"),
                              linebelow=None,
                              headerrow=DataRow(" ", " ", " "),
                              datarow=DataRow("|", "|", "|"),
                              padding=0,
                              usecolons=_format_defaults["usecolons"],
                              usehtmlattrs=_format_defaults["usehtmlattrs"],
                              with_header_hide=_format_defaults["with_header_hide"],
                              without_header_hide=_format_defaults["without_header_hide"]),
                  "grid":
                  TableFormat(lineabove=Line("+", "-", "+", "+"),
                              linebelowheader=Line("+", "=", "+", "+"),
                              linebetweenrows=Line("+", "-", "+", "+"),
                              linebelow=Line("+", "-", "+", "+"),
                              headerrow=DataRow("|", "|", "|"),
                              datarow=DataRow("|", "|", "|"),
                              padding=1,
                              usecolons=False,
                              usehtmlattrs=False,
                              with_header_hide=[],
                              without_header_hide=["linebelowheader"]),
                  "pipe":
                  TableFormat(lineabove=None,
                              linebelowheader=Line("|", "-", "|", "|"),
                              linebetweenrows=None,
                              linebelow=None,
                              headerrow=DataRow("|", "|", "|"),
                              datarow=DataRow("|", "|", "|"),
                              padding=1,
                              usecolons=True,
                              usehtmlattrs=False,
                              with_header_hide=[],
                              without_header_hide=[]),
                  "orgtbl":
                  TableFormat(lineabove=None,
                              linebelowheader=Line("|", "-", "+", "|"),
                              linebetweenrows=None,
                              linebelow=None,
                              headerrow=DataRow("|", "|", "|"),
                              datarow=DataRow("|", "|", "|"),
                              padding=1,
                              usecolons=False,
                              usehtmlattrs=False,
                              with_header_hide=[],
                              without_header_hide=["linebelowheader"]),
                  "rst":
                  TableFormat(lineabove=Line("", "=", "  ", ""),
                              linebelowheader=Line("", "=", "  ", ""),
                              linebetweenrows=None,
                              linebelow=Line("", "=", "  ", ""),
                              headerrow=DataRow("", "  ", ""),
                              datarow=DataRow("", "  ", ""),
                              padding=0,
                              usecolons=False,
                              usehtmlattrs=False,
                              with_header_hide=[],
                              without_header_hide=["linebelowheader"]),
                  "mediawiki":
                  TableFormat(lineabove=Line("{| class=\"wikitable\" style=\"text-align: left;\"",
                                             "", "", "\n|+ <!-- caption -->\n|-"),
                              linebelowheader=Line("|-", "", "", ""),
                              linebetweenrows=Line("|-", "", "", ""),
                              linebelow=Line("|}", "", "", ""),
                              headerrow=DataRow("!", "!!", ""),
                              datarow=DataRow("|", "||", ""),
                              padding=1,
                              usecolons=False,
                              usehtmlattrs=True,
                              with_header_hide=[],
                              without_header_hide=["linebelowheader"])}


_invisible_codes = re.compile("\x1b\[\d*m")  # ANSI color codes


def simple_separated_format(separator):
    """Construct a simple TableFormat with columns separated by a separator.

    >>> tsv = simple_separated_format("\t") ; \
        tabulate([["foo", 1], ["spam", 23]], tablefmt=tsv) == u'foo \\t 1\\nspam\\t23'
    True

    """
    return TableFormat(None, None, None, None,
                       headerrow=None, datarow=DataRow('', '\t', ''),
                       padding=_format_defaults["padding"],
                       usecolons=_format_defaults["usecolons"],
                       usehtmlattrs=_format_defaults["usehtmlattrs"],
                       with_header_hide=_format_defaults["with_header_hide"],
                       without_header_hide=_format_defaults["without_header_hide"])


def _isconvertible(conv, string):
    try:
        n = conv(string)
        return True
    except ValueError:
        return False


def _isnumber(string):
    """
    >>> _isnumber("123.45")
    True
    >>> _isnumber("123")
    True
    >>> _isnumber("spam")
    False
    """
    return _isconvertible(float, string)


def _isint(string):
    """
    >>> _isint("123")
    True
    >>> _isint("123.45")
    False
    """
    return type(string) is int or \
           (isinstance(string, _binary_type) or isinstance(string, _text_type)) and \
           _isconvertible(int, string)


def _type(string, has_invisible=True):
    """The least generic type (type(None), int, float, str, unicode).

    >>> _type(None) is type(None)
    True
    >>> _type("foo") is type("")
    True
    >>> _type("1") is type(1)
    True
    >>> _type(u'\x1b[31m42\x1b[0m') is type(42)
    True
    >>> _type('\x1b[31m42\x1b[0m') is type(42)
    True

    """

    if has_invisible and \
       (isinstance(string, _text_type) or isinstance(string, _binary_type)):
        string = _strip_invisible(string)

    if string is None:
        return _none_type
    elif _isint(string):
        return int
    elif _isnumber(string):
        return float
    elif isinstance(string, _binary_type):
        return _binary_type
    else:
        return _text_type


def _afterpoint(string):
    """Symbols after a decimal point, -1 if the string lacks the decimal point.

    >>> _afterpoint("123.45")
    2
    >>> _afterpoint("1001")
    -1
    >>> _afterpoint("eggs")
    -1
    >>> _afterpoint("123e45")
    2

    """
    if _isnumber(string):
        if _isint(string):
            return -1
        else:
            pos = string.rfind(".")
            pos = string.lower().rfind("e") if pos < 0 else pos
            if pos >= 0:
                return len(string) - pos - 1
            else:
                return -1  # no point
    else:
        return -1  # not a number


def _padleft(width, s, has_invisible=True):
    """Flush right.

    >>> _padleft(6, u'\u044f\u0439\u0446\u0430') == u'  \u044f\u0439\u0446\u0430'
    True

    """
    iwidth = width + len(s) - len(_strip_invisible(s)) if has_invisible else width
    fmt = u"{0:>%ds}" % iwidth
    return fmt.format(s)


def _padright(width, s, has_invisible=True):
    """Flush left.

    >>> _padright(6, u'\u044f\u0439\u0446\u0430') == u'\u044f\u0439\u0446\u0430  '
    True

    """
    iwidth = width + len(s) - len(_strip_invisible(s)) if has_invisible else width
    fmt = u"{0:<%ds}" % iwidth
    return fmt.format(s)


def _padboth(width, s, has_invisible=True):
    """Center string.

    >>> _padboth(6, u'\u044f\u0439\u0446\u0430') == u' \u044f\u0439\u0446\u0430 '
    True

    """
    iwidth = width + len(s) - len(_strip_invisible(s)) if has_invisible else width
    fmt = u"{0:^%ds}" % iwidth
    return fmt.format(s)


def _strip_invisible(s):
    "Remove invisible ANSI color codes."
    return re.sub(_invisible_codes, "", s)


def _visible_width(s):
    """Visible width of a printed string. ANSI color codes are removed.

    >>> _visible_width('\x1b[31mhello\x1b[0m'), _visible_width("world")
    (5, 5)

    """
    if isinstance(s, _text_type) or isinstance(s, _binary_type):
        return len(_strip_invisible(s))
    else:
        return len(_text_type(s))


def _align_column(strings, alignment, minwidth=0, has_invisible=True):
    """[string] -> [padded_string]

    >>> list(map(str,_align_column(["12.345", "-1234.5", "1.23", "1234.5", "1e+234", "1.0e234"], "decimal")))
    ['   12.345  ', '-1234.5    ', '    1.23   ', ' 1234.5    ', '    1e+234 ', '    1.0e234']

    """
    if alignment == "right":
        strings = [s.strip() for s in strings]
        padfn = _padleft
    elif alignment in "center":
        strings = [s.strip() for s in strings]
        padfn = _padboth
    elif alignment in "decimal":
        decimals = [_afterpoint(s) for s in strings]
        maxdecimals = max(decimals)
        strings = [s + (maxdecimals - decs) * " "
                   for s, decs in zip(strings, decimals)]
        padfn = _padleft
    else:
        strings = [s.strip() for s in strings]
        padfn = _padright

    if has_invisible:
        width_fn = _visible_width
    else:
        width_fn = len

    maxwidth = max(max(map(width_fn, strings)), minwidth)
    padded_strings = [padfn(maxwidth, s, has_invisible) for s in strings]
    return padded_strings


def _more_generic(type1, type2):
    types = { _none_type: 0, int: 1, float: 2, _text_type: 4 }
    invtypes = { 4: _text_type, 2: float, 1: int, 0: _none_type }
    moregeneric = max(types.get(type1, 4), types.get(type2, 4))
    return invtypes[moregeneric]


def _column_type(strings, has_invisible=True):
    """The least generic type all column values are convertible to.

    >>> _column_type(["1", "2"]) is _int_type
    True
    >>> _column_type(["1", "2.3"]) is _float_type
    True
    >>> _column_type(["1", "2.3", "four"]) is _text_type
    True
    >>> _column_type(["four", u'\u043f\u044f\u0442\u044c']) is _text_type
    True
    >>> _column_type([None, "brux"]) is _text_type
    True
    >>> _column_type([1, 2, None]) is _int_type
    True

    """
    types = [_type(s, has_invisible) for s in strings ]
    return reduce(_more_generic, types, int)


def _format(val, valtype, floatfmt, missingval=u""):
    """Format a value accoding to its type.

    Unicode is supported:

    >>> hrow = [u'\u0431\u0443\u043a\u0432\u0430', u'\u0446\u0438\u0444\u0440\u0430'] ; \
        tbl = [[u'\u0430\u0437', 2], [u'\u0431\u0443\u043a\u0438', 4]] ; \
        good_result = u'\\u0431\\u0443\\u043a\\u0432\\u0430      \\u0446\\u0438\\u0444\\u0440\\u0430\\n-------  -------\\n\\u0430\\u0437             2\\n\\u0431\\u0443\\u043a\\u0438           4' ; \
        tabulate(tbl, headers=hrow) == good_result
    True

    """
    if val is None:
        return missingval

    if valtype in [int, _binary_type, _text_type]:
        return u"{0}".format(__text_type(val))
    elif valtype is float:
        return format(float(val), floatfmt)
    else:
        return u"{0}".format(val)


def _align_header(header, alignment, width):
    if alignment == "left":
        return _padright(width, header)
    elif alignment == "center":
        return _padboth(width, header)
    else:
        return _padleft(width, header)


def _normalize_tabular_data(tabular_data, headers):
    """Transform a supported data type to a list of lists, and a list of headers.

    Supported tabular data types:

    * list-of-lists or another iterable of iterables

    * 2D NumPy arrays

    * dict of iterables (usually used with headers="keys")

    * pandas.DataFrame (usually used with headers="keys")

    The first row can be used as headers if headers="firstrow",
    column indices can be used as headers if headers="keys".

    """

    if hasattr(tabular_data, "keys") and hasattr(tabular_data, "values"):
        # dict-like and pandas.DataFrame?
        if hasattr(tabular_data.values, "__call__"):
            # likely a conventional dict
            keys = tabular_data.keys()
            rows = list(izip_longest(*tabular_data.values()))  # columns have to be transposed
        elif hasattr(tabular_data, "index"):
            # values is a property, has .index => it's likely a pandas.DataFrame (pandas 0.11.0)
            keys = tabular_data.keys()
            vals = tabular_data.values  # values matrix doesn't need to be transposed
            names = tabular_data.index
            rows = [[v]+list(row) for v,row in zip(names, vals)]
        else:
            raise ValueError("tabular data doesn't appear to be a dict or a DataFrame")

        if headers == "keys":
            headers = list(map(_text_type,keys))  # headers should be strings

    else:  # it's a usual an iterable of iterables, or a NumPy array
        rows = list(tabular_data)

        if headers == "keys" and len(rows) > 0:  # keys are column indices
            headers = list(map(_text_type, range(len(rows[0]))))

    # take headers from the first row if necessary
    if headers == "firstrow" and len(rows) > 0:
        headers = list(map(_text_type, rows[0])) # headers should be strings
        rows = rows[1:]

    headers = list(headers)
    rows = list(map(list,rows))

    # pad with empty headers for initial columns if necessary
    if headers and len(rows) > 0:
       nhs = len(headers)
       ncols = len(rows[0])
       if nhs < ncols:
           headers = [u""]*(ncols - nhs) + headers

    return rows, headers


def tabulate(tabular_data, headers=[], tablefmt="simple",
             floatfmt="g", numalign="decimal", stralign="left",
             missingval=u""):
    """Format a fixed width table for pretty printing.

    >>> print(tabulate([[1, 2.34], [-56, "8.999"], ["2", "10001"]]))
    ---  ---------
      1      2.34
    -56      8.999
      2  10001
    ---  ---------

    The first required argument (`tabular_data`) can be a
    list-of-lists (or another iterable or iterables), a dictionary of
    iterables, a two-dimensional NumPy array, or a Pandas' dataframe.


    Table headers
    -------------

    To print nice column headers, supply the second argument (`headers`):

      - `headers` can be an explicit list of column headers
      - if `headers="firstrow"`, then the first row of data is used
      - if `headers="keys"`, then dictionary keys or column indices are used

    Otherwise a headerless table is produced.

    If the number of headers is less than the number of columns, they
    are supposed to be names of the last columns. This is consistent
    with the plain-text format of R and Pandas' dataframes.

    >>> print(tabulate([["sex","age"],["Alice","F",24],["Bob","M",19]],
    ...       headers="firstrow"))
           sex      age
    -----  -----  -----
    Alice  F         24
    Bob    M         19


    Column alignment
    ----------------

    `tabulate` tries to detect column types automatically, and aligns
    the values properly. By default it aligns decimal points of the
    numbers (or flushes integer numbers to the right), and flushes
    everything else to the left. Possible column alignments
    (`numalign`, `stralign`) are: right, center, left, decimal (only
    for `numalign`).


    Table formats
    -------------

    `floatfmt` is a format specification used for columns which
    contain numeric data with a decimal point.

    `None` values are replaced with a `missingval` string:

    >>> print(tabulate([["spam", 1, None],
    ...                 ["eggs", 42, 3.14],
    ...                 ["other", None, 2.7]], missingval="?"))
    -----  --  ----
    spam    1  ?
    eggs   42  3.14
    other   ?  2.7
    -----  --  ----

    Various plain-text table formats (`tablefmt`) are supported:
    'plain', 'simple', 'grid', 'pipe', 'orgtbl', 'rst', and 'mediawiki'.

    "plain" format doesn't use any pseudographics to draw tables,
    it separates columns with a double space:

    >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]],
    ...                 ["strings", "numbers"], "plain"))
    strings      numbers
    spam         41.9999
    eggs        451

    >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], tablefmt="plain"))
    spam   41.9999
    eggs  451

    "simple" format is like Pandoc simple_tables:

    >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]],
    ...                 ["strings", "numbers"], "simple"))
    strings      numbers
    ---------  ---------
    spam         41.9999
    eggs        451

    >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], tablefmt="simple"))
    ----  --------
    spam   41.9999
    eggs  451
    ----  --------

    "grid" is similar to tables produced by Emacs table.el package or
    Pandoc grid_tables:

    >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]],
    ...                ["strings", "numbers"], "grid"))
    +-----------+-----------+
    | strings   |   numbers |
    +===========+===========+
    | spam      |   41.9999 |
    +-----------+-----------+
    | eggs      |  451      |
    +-----------+-----------+

    >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], tablefmt="grid"))
    +------+----------+
    | spam |  41.9999 |
    +------+----------+
    | eggs | 451      |
    +------+----------+

    "pipe" is like tables in PHP Markdown Extra extension or Pandoc
    pipe_tables:

    >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]],
    ...                ["strings", "numbers"], "pipe"))
    | strings   |   numbers |
    |:----------|----------:|
    | spam      |   41.9999 |
    | eggs      |  451      |

    >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], tablefmt="pipe"))
    |:-----|---------:|
    | spam |  41.9999 |
    | eggs | 451      |

    "orgtbl" is like tables in Emacs org-mode and orgtbl-mode. They
    are slightly different from "pipe" format by not using colons to
    define column alignment, and using a "+" sign to indicate line
    intersections:

    >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]],
    ...                ["strings", "numbers"], "orgtbl"))
    | strings   |   numbers |
    |-----------+-----------|
    | spam      |   41.9999 |
    | eggs      |  451      |


    >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], tablefmt="orgtbl"))
    | spam |  41.9999 |
    | eggs | 451      |

    "rst" is like a simple table format from reStructuredText; please
    note that reStructuredText accepts also "grid" tables:

    >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]],
    ...                ["strings", "numbers"], "rst"))
    =========  =========
    strings      numbers
    =========  =========
    spam         41.9999
    eggs        451
    =========  =========

    >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], tablefmt="rst"))
    ====  ========
    spam   41.9999
    eggs  451
    ====  ========

    "mediawiki" produces a table markup used in Wikipedia and on other
    MediaWiki-based sites:

    >>> print(tabulate([["strings", "numbers"], ["spam", 41.9999], ["eggs", "451.0"]],
    ...                headers="firstrow", tablefmt="mediawiki"))
    {| class="wikitable" style="text-align: left;"
    |+ <!-- caption -->
    |-
    ! strings   !! align="right"|   numbers
    |-
    | spam      || align="right"|   41.9999
    |-
    | eggs      || align="right"|  451
    |}

    >>> print(tabulate([["eggs", 42], ["spam", 23]], tablefmt="mediawiki", stralign="left"))
    {| class="wikitable" style="text-align: left;"
    |+ <!-- caption -->
    |-
    | eggs || align="right"| 42
    |-
    | spam || align="right"| 23
    |}


    """

    list_of_lists, headers = _normalize_tabular_data(tabular_data, headers)

    # optimization: look for ANSI control codes once,
    # enable smart width functions only if a control code is found
    plain_text = u'\n'.join(['\t'.join(map(_text_type, headers))] + \
                            [u'\t'.join(map(_text_type, row)) for row in list_of_lists])
    has_invisible = re.search(_invisible_codes, plain_text)
    if has_invisible:
        width_fn = _visible_width
    else:
        width_fn = len

    # format rows and columns, convert numeric values to strings
    cols = list(zip(*list_of_lists))
    coltypes = list(map(_column_type, cols))
    cols = [[_format(v, ct, floatfmt, missingval) for v in c]
             for c,ct in zip(cols, coltypes)]

    # align columns
    aligns = [numalign if ct in [int,float] else stralign for ct in coltypes]
    minwidths = [width_fn(h)+2 for h in headers] if headers else [0]*len(cols)
    cols = [_align_column(c, a, minw, has_invisible)
            for c, a, minw in zip(cols, aligns, minwidths)]

    if headers:
        # align headers and add headers
        minwidths = [max(minw, width_fn(c[0])) for minw, c in zip(minwidths, cols)]
        headers = [_align_header(h, a, minw)
                   for h, a, minw in zip(headers, aligns, minwidths)]
        rows = list(zip(*cols))
    else:
        minwidths = [width_fn(c[0]) for c in cols]
        rows = list(zip(*cols))

    if not isinstance(tablefmt, TableFormat):
        tablefmt = _table_formats.get(tablefmt, _table_formats["simple"])

    return _format_table(tablefmt, headers, rows, minwidths, aligns)


def _build_row(cells, padding, begin, sep, end):
    "Return a string which represents a row of data cells."
    pad = u" "*padding
    padded_cells = [pad + cell + pad for cell in cells]
    return (begin + sep.join(padded_cells) + end).rstrip()


def _build_line(colwidths, padding, begin, fill, sep,  end):
    "Return a string which represents a horizontal line."
    cells = [fill*(w + 2*padding) for w in colwidths]
    return _build_row(cells, 0, begin, sep, end)


def _mediawiki_cell_attrs(row, colaligns):
    "Prefix every cell in a row with an HTML alignment attribute."
    alignment = { "left":    '',
                  "right":   'align="right"| ',
                  "center":  'align="center"| ',
                  "decimal": 'align="right"| ' }
    row2 = [alignment[a] + c for c, a in zip(row, colaligns)]
    return row2


def _line_segment_with_colons(linefmt, align, colwidth):
    """Return a segment of a horizontal line with optional colons which
    indicate column's alignment (as in `pipe` output format)."""
    fill = linefmt.hline
    w = colwidth
    if align in ["right", "decimal"]:
        return (fill[0] * (w - 1)) + ":"
    elif align == "center":
        return ":" + (fill[0] * (w - 2)) + ":"
    elif align == "left":
        return ":" + (fill[0] * (w - 1))
    else:
        return fill[0] * w


def _format_table(fmt, headers, rows, colwidths, colaligns):
    """Produce a plain-text representation of the table."""
    lines = []
    hidden = fmt.with_header_hide if headers else fmt.without_header_hide
    pad = fmt.padding
    headerrow = fmt.headerrow if fmt.headerrow else fmt.datarow

    if fmt.usehtmlattrs:
        headers = _mediawiki_cell_attrs(headers, colaligns)
        rows = [_mediawiki_cell_attrs(row, colaligns) for row in rows]

    if fmt.lineabove and "lineabove" not in hidden:
        lines.append(_build_line(colwidths, pad, *fmt.lineabove))

    if headers:
        lines.append(_build_row(headers, pad, *headerrow))

    if fmt.linebelowheader and "linebelowheader" not in hidden:
        begin, fill, sep, end = fmt.linebelowheader
        if fmt.usecolons:
            segs = [_line_segment_with_colons(fmt.linebelowheader, a, w + 2*pad)
                    for w,a in zip(colwidths, colaligns)]
            lines.append(_build_row(segs, 0, begin, sep, end))
        else:
            lines.append(_build_line(colwidths, pad, *fmt.linebelowheader))

    if rows and fmt.linebetweenrows and "linebetweenrows" not in hidden:
        # initial rows with a line below
        for row in rows[:-1]:
            lines.append(_build_row(row, pad, *fmt.datarow))
            lines.append(_build_line(colwidths, pad, *fmt.linebetweenrows))
        # the last row without a line below
        lines.append(_build_row(rows[-1], pad, *fmt.datarow))
    else:
        for row in rows:
            lines.append(_build_row(row, pad, *fmt.datarow))

    if fmt.linebelow and "linebelow" not in hidden:
        lines.append(_build_line(colwidths, pad, *fmt.linebelow))

    return "\n".join(lines)
  0707010005ca16000081a40000000000000000000000015ab2183900000015000000660000000000000000000000000000002600000000root/usr/share/opensvc/lib/version.py version = "1.9-1489"
   0707010005ca17000081b40000000000000000000000015ab217540000b756000000660000000000000000000000000000002200000000root/usr/share/opensvc/lib/wmi.py """
Windows Management Instrumentation (WMI) is Microsoft's answer to
the DMTF's Common Information Model. It allows you to query just
about any conceivable piece of information from any computer which
is running the necessary agent and over which have you the
necessary authority.

Since the COM implementation doesn't give much away to Python
programmers, I've wrapped it in some lightweight classes with
some getattr / setattr magic to ease the way. In particular:

* The :class:`_wmi_namespace` object itself will determine its classes
  and allow you to return all instances of any of them by
  using its name as an attribute::

    disks = wmi.WMI ().Win32_LogicalDisk ()

* In addition, you can specify what would become the WHERE clause
  as keyword parameters::

    fixed_disks = wmi.WMI ().Win32_LogicalDisk (DriveType=3)

* The objects returned by a WMI lookup are wrapped in a Python
  class which determines their methods and classes and allows
  you to access them as though they were Python classes. The
  methods only allow named parameters::

    for p in wmi.WMI ().Win32_Process (Name="notepad.exe"):
      p.Terminate (Result=1)

* Doing a print() on one of the WMI objects will result in its
  `GetObjectText\_` method being called, which usually produces
  a meaningful printout of current values.
  The repr of the object will include its full WMI path,
  which lets you get directly to it if you need to.

* You can get the associators and references of an object as
  a list of python objects by calling the associators () and
  references () methods on a WMI Python object::

    for p in wmi.WMI ().Win32_Process (Name="notepad.exe"):
      for r in p.references ():
        print(r)

  ..  note::
      Don't do this on a Win32_ComputerSystem object; it will
      take all day and kill your machine!


* WMI classes (as opposed to instances) are first-class
  objects, so you can get hold of a class, and call
  its methods or set up a watch against it::

    process = wmi.WMI ().Win32_Process
    process.Create (CommandLine="notepad.exe")

* To make it easier to use in embedded systems and py2exe-style
  executable wrappers, the module will not force early Dispatch.
  To do this, it uses a handy hack by Thomas Heller for easy access
  to constants.

Typical usage will be::

  import wmi

  vodev1 = wmi.WMI ("vodev1")
  for disk in vodev1.Win32_LogicalDisk ():
    if disk.DriveType == 3:
      space = 100 * long (disk.FreeSpace) / long (disk.Size)
      print("%s has %d%% free" % (disk.Name, space))

Many thanks, obviously to Mark Hammond for creating the win32all
extensions, but also to Alex Martelli and Roger Upole, whose
c.l.py postings pointed me in the right direction.
Thanks especially in release 1.2 to Paul Tiemann for his code
contributions and robust testing.
"""
__VERSION__ = __version__ = "1.4.9"

_DEBUG = False

import sys
import datetime
import re
import struct
import warnings

from win32com.client import GetObject, Dispatch
import pywintypes

def signed_to_unsigned (signed):
  """Convert a (possibly signed) long to unsigned hex. Useful
  when converting a COM error code to the more conventional
  8-digit hex::

    print("%08X" % signed_to_unsigned (-2147023174))
  """
  unsigned, = struct.unpack ("L", struct.pack ("l", signed))
  return unsigned

class SelfDeprecatingDict (object):
  """Provides for graceful degradation of objects which
  are currently dictionaries (and therefore accessed via
  `.keys`, `.items`, etc.) into lists. Wraps an existing
  `dict` and allows it to be addressed as a `dict` or as a
  `list` during an interregnum, issuing a `DeprecationWarning`
  if accessed as a `dict`.
  """

  dict_only = set (dir (dict)).difference (dir (list))

  def __init__ (self, dictlike):
    self.dict = dict (dictlike)
    self.list = list (self.dict)

  def __getattr__ (self, attribute):
    if attribute in self.dict_only:
      warnings.warn ("In future this will be a list and not a dictionary", DeprecationWarning)
      return getattr (self.dict, attribute)
    else:
      return getattr (self.list, attribute)

  def __iter__ (self):
    return iter (self.list)

  def __str__ (self):
    return str (self.list)

  def __repr__ (self):
    return repr (self.list)

  def __getitem__ (self, item):
    try:
      return self.list[item]
    except TypeError:
      return self.dict[item]

class ProvideConstants (object):
  """When called on a ``win32com.client.Dispatch`` object,
  provides lazy access to constants defined in the typelib.
  They can then be accessed as attributes of the :attr:`_constants`
  property. (From Thomas Heller on c.l.py).
  """
  def __init__(self, comobj):
    comobj.__dict__["_constants"] = self
    self.__typecomp = \
    comobj._oleobj_.GetTypeInfo().GetContainingTypeLib()[0].GetTypeComp()

  def __getattr__(self, name):
    if name.startswith("__") and name.endswith("__"):
     raise AttributeError (name)
    result = self.__typecomp.Bind(name)
    if not result[0]:
     raise AttributeError (name)
    return result[1].value

obj = GetObject ("winmgmts:")
ProvideConstants (obj)

wbemErrInvalidQuery = obj._constants.wbemErrInvalidQuery
wbemErrTimedout = obj._constants.wbemErrTimedout
wbemFlagReturnImmediately = obj._constants.wbemFlagReturnImmediately
wbemFlagForwardOnly = obj._constants.wbemFlagForwardOnly

#
# Exceptions
#
class x_wmi (Exception):
  """Ancestor of all wmi-related exceptions. Keeps track of
  an info message and the underlying COM error if any, exposed
  as the :attr:`com_error` attribute.
  """
  def __init__ (self, info="", com_error=None):
    self.info = info
    self.com_error = com_error

  def __str__ (self):
    return "<x_wmi: %s %s>" % (
      self.info or "Unexpected COM Error",
      self.com_error or "(no underlying exception)"
    )

class x_wmi_invalid_query (x_wmi):
  "Raised when a WMI returns `wbemErrInvalidQuery`"
  pass

class x_wmi_timed_out (x_wmi):
  "Raised when a watcher times out"
  pass

class x_wmi_no_namespace (x_wmi):
  """Raised when an attempt is made to query or watch
  from a class without a namespace.
  """
  pass

class x_access_denied (x_wmi):
  "Raised when WMI raises 80070005"
  pass

class x_wmi_authentication (x_wmi):
  "Raised when an invalid combination of authentication properties is attempted when connecting"
  pass

class x_wmi_uninitialised_thread (x_wmi):
  """Raised when WMI returns 800401E4 on connection, usually
  indicating that no COM threading model has been initialised
  """
  pass

WMI_EXCEPTIONS = {
  signed_to_unsigned (wbemErrInvalidQuery) : x_wmi_invalid_query,
  signed_to_unsigned (wbemErrTimedout) : x_wmi_timed_out,
  0x80070005 : x_access_denied,
  0x80041003 : x_access_denied,
  0x800401E4 : x_wmi_uninitialised_thread,
}

def handle_com_error (err=None):
  """Convenience wrapper for displaying all manner of COM errors.
  Raises a :exc:`x_wmi` exception with more useful information attached

  :param err: The structure attached to a `pywintypes.com_error`
  """
  if err is None:
    _, err, _ = sys.exc_info ()
  hresult_code, hresult_name, additional_info, parameter_in_error = err.args
  hresult_code = signed_to_unsigned (hresult_code)
  exception_string = ["%s - %s" % (hex (hresult_code), hresult_name)]
  scode = None
  if additional_info:
    wcode, source_of_error, error_description, whlp_file, whlp_context, scode = additional_info
    scode = signed_to_unsigned (scode)
    exception_string.append ("  Error in: %s" % source_of_error)
    exception_string.append ("  %s - %s" % (hex (scode), (error_description or "").strip ()))
  for error_code, klass in WMI_EXCEPTIONS.items ():
    if error_code in (hresult_code, scode):
      break
  else:
    klass = x_wmi
  raise klass (com_error=err)


BASE = datetime.datetime (1601, 1, 1)
def from_1601 (ns100):
  return BASE + datetime.timedelta (microseconds=int (ns100) / 10)

def from_time (year=None, month=None, day=None, hours=None, minutes=None, seconds=None, microseconds=None, timezone=None):
  """Convenience wrapper to take a series of date/time elements and return a WMI time
  of the form `yyyymmddHHMMSS.mmmmmm+UUU`. All elements may be int, string or
  omitted altogether. If omitted, they will be replaced in the output string
  by a series of stars of the appropriate length.

  :param year: The year element of the date/time
  :param month: The month element of the date/time
  :param day: The day element of the date/time
  :param hours: The hours element of the date/time
  :param minutes: The minutes element of the date/time
  :param seconds: The seconds element of the date/time
  :param microseconds: The microseconds element of the date/time
  :param timezone: The timeezone element of the date/time

  :returns: A WMI datetime string of the form: `yyyymmddHHMMSS.mmmmmm+UUU`
  """
  def str_or_stars (i, length):
    if i is None:
      return "*" * length
    else:
      return str (i).rjust (length, "0")

  wmi_time = ""
  wmi_time += str_or_stars (year, 4)
  wmi_time += str_or_stars (month, 2)
  wmi_time += str_or_stars (day, 2)
  wmi_time += str_or_stars (hours, 2)
  wmi_time += str_or_stars (minutes, 2)
  wmi_time += str_or_stars (seconds, 2)
  wmi_time += "."
  wmi_time += str_or_stars (microseconds, 6)
  if timezone >= 0:
    wmi_time += "+"
  else:
    wmi_time += "-"
    timezone = abs (timezone)
  wmi_time += str_or_stars (timezone, 3)

  return wmi_time

def to_time (wmi_time):
  """Convenience wrapper to take a WMI datetime string of the form
  yyyymmddHHMMSS.mmmmmm+UUU and return a 9-tuple containing the
  individual elements, or None where string contains placeholder
  stars.

  :param wmi_time: The WMI datetime string in `yyyymmddHHMMSS.mmmmmm+UUU` format

  :returns: A 9-tuple of (year, month, day, hours, minutes, seconds, microseconds, timezone)
  """
  def int_or_none (s, start, end):
    try:
      return int (s[start:end])
    except ValueError:
      return None

  year = int_or_none (wmi_time, 0, 4)
  month = int_or_none (wmi_time, 4, 6)
  day = int_or_none (wmi_time, 6, 8)
  hours = int_or_none (wmi_time, 8, 10)
  minutes = int_or_none (wmi_time, 10, 12)
  seconds = int_or_none (wmi_time, 12, 14)
  microseconds = int_or_none (wmi_time, 15, 21)
  timezone = wmi_time[22:]
  if timezone == "***":
    timezone = None

  return year, month, day, hours, minutes, seconds, microseconds, timezone

def _set (obj, attribute, value):
  """Helper function to add an attribute directly into the instance
  dictionary, bypassing possible `__getattr__` calls

  :param obj: Any python object
  :param attribute: String containing attribute name
  :param value: Any python object
  """
  obj.__dict__[attribute] = value

class _wmi_method:
  """A currying sort of wrapper around a WMI method name. It
  abstract's the method's parameters and can be called like
  a normal Python object passing in the parameter values.

  Output parameters are returned from the call as a tuple.
  In addition, the docstring is set up as the method's
  signature, including an indication as to whether any
  given parameter is expecting an array, and what
  special privileges are required to call the method.
  """

  def __init__ (self, ole_object, method_name):
    """
    :param ole_object: The WMI class/instance whose method is to be called
    :param method_name: The name of the method to be called
    """
    try:
      self.ole_object = Dispatch (ole_object)
      self.method = ole_object.Methods_ (method_name)
      self.qualifiers = {}
      for q in self.method.Qualifiers_:
        self.qualifiers[q.Name] = q.Value
      self.provenance = "\n".join (self.qualifiers.get ("MappingStrings", []))

      self.in_parameters = self.method.InParameters
      self.out_parameters = self.method.OutParameters
      if self.in_parameters is None:
        self.in_parameter_names = []
      else:
        self.in_parameter_names = [(i.Name, i.IsArray) for i in self.in_parameters.Properties_]
      if self.out_parameters is None:
        self.out_parameter_names = []
      else:
        self.out_parameter_names = [(i.Name, i.IsArray) for i in self.out_parameters.Properties_]

      doc = "%s (%s) => (%s)" % (
        method_name,
        ", ".join ([name + ("", "[]")[is_array] for (name, is_array) in self.in_parameter_names]),
        ", ".join ([name + ("", "[]")[is_array] for (name, is_array) in self.out_parameter_names])
      )
      privileges = self.qualifiers.get ("Privileges", [])
      if privileges:
        doc += " | Needs: " + ", ".join (privileges)
      self.__doc__ = doc
    except pywintypes.com_error:
      handle_com_error ()

  def __call__ (self, *args, **kwargs):
    """Execute the call to a WMI method, returning
    a tuple (even if is of only one value) containing
    the out and return parameters.
    """
    try:
      if self.in_parameters:
        parameter_names = {}
        for name, is_array in self.in_parameter_names:
          parameter_names[name] = is_array

        parameters = self.in_parameters

        #
        # Check positional parameters first
        #
        for n_arg in range (len (args)):
          arg = args[n_arg]
          parameter = parameters.Properties_[n_arg]
          if parameter.IsArray:
            try: list (arg)
            except TypeError: raise TypeError ("parameter %d must be iterable" % n_arg)
          parameter.Value = arg

        #
        # If any keyword param supersedes a positional one,
        # it'll simply overwrite it.
        #
        for k, v in kwargs.items ():
          is_array = parameter_names.get (k)
          if is_array is None:
            raise AttributeError ("%s is not a valid parameter for %s" % (k, self.__doc__))
          else:
            if is_array:
              try: list (v)
              except TypeError: raise TypeError ("%s must be iterable" % k)
          parameters.Properties_ (k).Value = v

        result = self.ole_object.ExecMethod_ (self.method.Name, self.in_parameters)
      else:
        result = self.ole_object.ExecMethod_ (self.method.Name)

      results = []
      for name, is_array in self.out_parameter_names:
        value = result.Properties_ (name).Value
        if is_array:
          #
          # Thanks to Jonas Bjering for bug report and patch
          #
          results.append (list (value or []))
        else:
          results.append (value)
      return tuple (results)

    except pywintypes.com_error:
      handle_com_error ()

  def __repr__ (self):
    return "<function %s>" % self.__doc__

class _wmi_property (object):

  def __init__ (self, property):
    self.property = property
    self.name = property.Name
    self.value = property.Value
    self.qualifiers = dict ((q.Name, q.Value) for q in property.Qualifiers_)
    self.type = self.qualifiers.get ("CIMTYPE", None)

  def set (self, value):
    self.property.Value = value

  def __repr__ (self):
    return "<wmi_property: %s>" % self.name

  def __getattr__ (self, attr):
    return getattr (self.property, attr)

#
# class _wmi_object
#
class _wmi_object:
  """The heart of the WMI module: wraps the objects returned by COM
  ISWbemObject interface and provide readier access to their properties
  and methods resulting in a more Pythonic interface. Not usually
  instantiated directly, rather as a result of calling a :class:`_wmi_class`
  on the parent :class:`_wmi_namespace`.

  If you get hold of a WMI-related COM object from some other
  source than this module, you can wrap it in one of these objects
  to get the benefits of the module::

    import win32com.client
    import wmi

    wmiobj = win32com.client.GetObject ("winmgmts:Win32_LogicalDisk.DeviceID='C:'")
    c_drive = wmi._wmi_object (wmiobj)
    print(c_drive)
  """

  def __init__ (self, ole_object, instance_of=None, fields=[], property_map={}):
    try:
      _set (self, "ole_object", ole_object)
      _set (self, "id", ole_object.Path_.DisplayName.lower ())
      _set (self, "_instance_of", instance_of)
      _set (self, "properties", {})
      _set (self, "methods", {})
      _set (self, "property_map", property_map)
      _set (self, "_associated_classes", None)
      _set (self, "_keys", None)

      if fields:
        for field in fields:
          self.properties[field] = None
      else:
        for p in ole_object.Properties_:
          self.properties[p.Name] = None

      for m in ole_object.Methods_:
        self.methods[m.Name] = None

      _set (self, "_properties", self.properties.keys ())
      _set (self, "_methods", self.methods.keys ())
      _set (self, "qualifiers", dict ((q.Name, q.Value) for q in self.ole_object.Qualifiers_))

    except pywintypes.com_error:
      handle_com_error ()

  def __lt__ (self, other):
    return self.id < other.id

  def __str__ (self):
    """For a call to print([object]) return the OLE description
    of the properties / values of the object
    """
    try:
      return self.ole_object.GetObjectText_ ()
    except pywintypes.com_error:
      handle_com_error ()

  def __repr__ (self):
    """
    Indicate both the fact that this is a wrapped WMI object
    and the WMI object's own identifying class.
    """
    try:
      return "<%s: %s>" % (self.__class__.__name__, self.Path_.Path.encode ("ascii", "backslashreplace"))
    except pywintypes.com_error:
      handle_com_error ()

  def _cached_properties (self, attribute):
    if self.properties[attribute] is None:
      self.properties[attribute] = _wmi_property (self.ole_object.Properties_ (attribute))
    return self.properties[attribute]

  def _cached_methods (self, attribute):
    if self.methods[attribute] is None:
      self.methods[attribute] = _wmi_method (self.ole_object, attribute)
    return self.methods[attribute]

  def __getattr__ (self, attribute):
    """
    Attempt to pass attribute calls to the proxied COM object.
    If the attribute is recognised as a property, return its value;
    if it is recognised as a method, return a method wrapper which
    can then be called with parameters; otherwise pass the lookup
    on to the underlying object.
    """
    try:
      if attribute in self.properties:
        property = self._cached_properties (attribute)
        factory = self.property_map.get (attribute, self.property_map.get (property.type, lambda x: x))
        value = factory (property.value)
        #
        # If this is an association, certain of its properties
        # are actually the paths to the aspects of the association,
        # so translate them automatically into WMI objects.
        #
        if property.type.startswith ("ref:"):
          return WMI (moniker=value)
        else:
          return value
      elif attribute in self.methods:
        return self._cached_methods (attribute)
      else:
        return getattr (self.ole_object, attribute)
    except pywintypes.com_error:
      handle_com_error ()

  def __setattr__ (self, attribute, value):
    """If the attribute to be set is valid for the proxied
    COM object, set that objects's parameter value; if not,
    raise an exception.
    """
    try:
      if attribute in self.properties:
        self._cached_properties (attribute).set (value)
        if self.ole_object.Path_.Path:
          self.ole_object.Put_ ()
      else:
        raise AttributeError (attribute)
    except pywintypes.com_error:
      handle_com_error ()

  def __eq__ (self, other):
    return self.id == other.id

  def __hash__ (self):
    return hash (self.id)

  def _getAttributeNames (self):
     """Return list of methods/properties for IPython completion"""
     attribs = [str (x) for x in self.methods.keys ()]
     attribs.extend ([str (x) for x in self.properties.keys ()])
     return attribs

  def _get_keys (self):
    """A WMI object is uniquely defined by a set of properties
    which constitute its keys. Lazily retrieves the keys for this
    instance or class.

    :returns: list of key property names
    """
    # NB You can get the keys of an instance more directly, via
    # Path\_.Keys but this doesn't apply to classes. The technique
    # here appears to work for both.
    if self._keys is None:
      _set (self, "_keys", [])
      for property in self.ole_object.Properties_:
        for qualifier in property.Qualifiers_:
          if qualifier.Name == "key" and qualifier.Value:
            self._keys.append (property.Name)
    return self._keys
  keys = property (_get_keys)

  def wmi_property (self, property_name):
    """Return the cached object representing one property
    of this object
    """
    return _wmi_property (self.ole_object.Properties_ (property_name))

  def put (self):
    """Push all outstanding property updates back to the
    WMI database.
    """
    self.ole_object.Put_ ()

  def set (self, **kwargs):
    """Set several properties of the underlying object
    at one go. This is particularly useful in combination
    with the new () method below. However, an instance
    which has been spawned in this way won't have enough
    information to write pack, so only try if the
    instance has a path.
    """
    if kwargs:
      try:
        for attribute, value in kwargs.items ():
          if attribute in self.properties:
            self._cached_properties (attribute).set (value)
          else:
            raise AttributeError (attribute)
        #
        # Only try to write the attributes
        #  back if the object exists.
        #
        if self.ole_object.Path_.Path:
          self.ole_object.Put_ ()
      except pywintypes.com_error:
        handle_com_error ()

  def path (self):
    """Return the WMI URI to this object. Can be used to
    determine the path relative to the parent namespace::

      pp0 = wmi.WMI ().Win32_ParallelPort ()[0]
      print(pp0.path ().RelPath)

    ..  Do more with this
    """
    try:
      return self.ole_object.Path_
    except pywintypes.com_error:
      handle_com_error ()

  def derivation (self):
    """Return a tuple representing the object derivation for
    this object, with the most specific object first::

      pp0 = wmi.WMI ().Win32_ParallelPort ()[0]
      print(' <- '.join (pp0.derivation ()))
    """
    try:
      return self.ole_object.Derivation_
    except pywintypes.com_error:
      handle_com_error ()

  def _cached_associated_classes (self):
    if self._associated_classes is None:
      if isinstance (self, _wmi_class):
        params = {'bSchemaOnly' : True}
      else:
        params = {'bClassesOnly' : True}
      try:
        associated_classes = dict (
          (assoc.Path_.Class, _wmi_class (self._namespace, assoc)) for
            assoc in self.ole_object.Associators_ (**params)
        )
        _set (self, "_associated_classes", associated_classes)
      except pywintypes.com_error:
        handle_com_error ()

    return self._associated_classes
  associated_classes = property (_cached_associated_classes)

  def associators (self, wmi_association_class="", wmi_result_class=""):
    """Return a list of objects related to this one, optionally limited
    either by association class (ie the name of the class which relates
    them) or by result class (ie the name of the class which would be
    retrieved)::

      c = wmi.WMI ()
      pp = c.Win32_ParallelPort ()[0]

      for i in pp.associators (wmi_association_class="Win32_PortResource"):
        print(i)

      for i in pp.associators (wmi_result_class="Win32_PnPEntity"):
        print(i)
    """
    try:
      return [
        _wmi_object (i) for i in \
          self.ole_object.Associators_ (
           strAssocClass=wmi_association_class,
           strResultClass=wmi_result_class
         )
      ]
    except pywintypes.com_error:
      handle_com_error ()

  def references (self, wmi_class=""):
    """Return a list of associations involving this object, optionally
    limited by the result class (the name of the association class).

    NB Associations are treated specially; although WMI only returns
    the string corresponding to the instance of each associated object,
    this module will automatically convert that to the object itself::

      c =  wmi.WMI ()
      sp = c.Win32_SerialPort ()[0]

      for i in sp.references ():
        print(i)

      for i in sp.references (wmi_class="Win32_SerialPortSetting"):
        print(i)
    """
    #
    # FIXME: Allow an actual class to be passed in, using
    # its .Path_.RelPath property to determine the string
    #
    try:
      return [_wmi_object (i) for i in self.ole_object.References_ (strResultClass=wmi_class)]
    except pywintypes.com_error:
      handle_com_error ()

#
# class _wmi_event
#
class _wmi_event (_wmi_object):
  """Slight extension of the _wmi_object class to allow
  objects which are the result of events firing to return
  extra information such as the type of event.
  """
  event_type_re = re.compile ("__Instance(Creation|Modification|Deletion)Event")
  def __init__ (self, event, event_info, fields=[]):
    _wmi_object.__init__ (self, event, fields=fields)
    _set (self, "event_type", None)
    _set (self, "timestamp", None)
    _set (self, "previous", None)

    if event_info:
      event_type = self.event_type_re.match (event_info.Path_.Class).group (1).lower ()
      _set (self, "event_type", event_type)
      if hasattr (event_info, "TIME_CREATED"):
        _set (self, "timestamp", from_1601 (event_info.TIME_CREATED))
      if hasattr (event_info, "PreviousInstance"):
        _set (self, "previous", event_info.PreviousInstance)

#
# class _wmi_class
#
class _wmi_class (_wmi_object):
  """Currying class to assist in issuing queries against
   a WMI namespace. The idea is that when someone issues
   an otherwise unknown method against the WMI object, if
   it matches a known WMI class a query object will be
   returned which may then be called with one or more params
   which will form the WHERE clause::

    c = wmi.WMI ()
    c_drives = c.Win32_LogicalDisk (Name='C:')
  """
  def __init__ (self, namespace, wmi_class):
    _wmi_object.__init__ (self, wmi_class)
    _set (self, "_class_name", wmi_class.Path_.Class)
    if namespace:
      _set (self, "_namespace", namespace)
    else:
      class_moniker = wmi_class.Path_.DisplayName
      winmgmts, namespace_moniker, class_name = class_moniker.split (":")
      namespace = _wmi_namespace (GetObject (winmgmts + ":" + namespace_moniker), False)
      _set (self, "_namespace", namespace)

  def __getattr__ (self, attribute):
    try:
      if attribute in self.properties:
        return _wmi_property (self.Properties_ (attribute))
      else:
        return _wmi_object.__getattr__ (self, attribute)
    except pywintypes.com_error:
      handle_com_error ()


  def query (self, fields=[], **where_clause):
    """Make it slightly easier to query against the class,
     by calling the namespace's query with the class preset.
     Won't work if the class has been instantiated directly.
    """
    #
    # FIXME: Not clear if this can ever happen
    #
    if self._namespace is None:
      raise x_wmi_no_namespace ("You cannot query directly from a WMI class")

    try:
      field_list = ", ".join (fields) or "*"
      wql = "SELECT " + field_list + " FROM " + self._class_name
      if where_clause:
        wql += " WHERE " + " AND ". join (["%s = %r" % (k, str (v)) for k, v in where_clause.items ()])
      return self._namespace.query (wql, self, fields)
    except pywintypes.com_error:
      handle_com_error ()

  __call__ = query

  def watch_for (
    self,
    notification_type="operation",
    delay_secs=1,
    fields=[],
    **where_clause
  ):
    if self._namespace is None:
      raise x_wmi_no_namespace ("You cannot watch directly from a WMI class")

    valid_notification_types = ("operation", "creation", "deletion", "modification")
    if notification_type.lower () not in valid_notification_types:
      raise x_wmi ("notification_type must be one of %s" % ", ".join (valid_notification_types))

    return self._namespace.watch_for (
      notification_type=notification_type,
      wmi_class=self,
      delay_secs=delay_secs,
      fields=fields,
      **where_clause
    )

  def instances (self):
    """Return a list of instances of the WMI class
    """
    try:
      return [_wmi_object (instance, self) for instance in self.Instances_ ()]
    except pywintypes.com_error:
      handle_com_error ()

  def new (self, **kwargs):
    """This is the equivalent to the raw-WMI SpawnInstance\_
    method. Note that there are relatively few uses for
    this, certainly fewer than you might imagine. Most
    classes which need to create a new *real* instance
    of themselves, eg Win32_Process, offer a .Create
    method. SpawnInstance\_ is generally reserved for
    instances which are passed as parameters to such
    `.Create` methods, a common example being the
    `Win32_SecurityDescriptor`, passed to `Win32_Share.Create`
    and other instances which need security.

    The example here is `Win32_ProcessStartup`, which
    controls the shown/hidden state etc. of a new
    `Win32_Process` instance::

      import win32con
      import wmi
      c = wmi.WMI ()
      startup = c.Win32_ProcessStartup.new (ShowWindow=win32con.SW_SHOWMINIMIZED)
      pid, retval = c.Win32_Process.Create (
        CommandLine="notepad.exe",
        ProcessStartupInformation=startup
      )

    ..  warning::
        previous versions of this docstring illustrated using this function
        to create a new process. This is *not* a good example of its use;
        it is better handled with something like the example above.
    """
    try:
      obj = _wmi_object (self.SpawnInstance_ (), self)
      obj.set (**kwargs)
      return obj
    except pywintypes.com_error:
      handle_com_error ()

#
# class _wmi_result
#
class _wmi_result:
  """Simple, data only result for targeted WMI queries which request
  data only result classes via fetch_as_classes.
  """
  def __init__(self, obj, attributes):
    if attributes:
      for attr in attributes:
        self.__dict__[attr] = obj.Properties_ (attr).Value
    else:
      for p in obj.Properties_:
        attr = p.Name
        self.__dict__[attr] = obj.Properties_(attr).Value

#
# class WMI
#
class _wmi_namespace:
  """A WMI root of a computer system. The classes attribute holds a list
  of the classes on offer. This means you can explore a bit with
  things like this::

    c = wmi.WMI ()
    for i in c.classes:
      if "user" in i.lower ():
        print(i)
  """
  def __init__ (self, namespace, find_classes):
    _set (self, "_namespace", namespace)
    #
    # wmi attribute preserved for backwards compatibility
    #
    _set (self, "wmi", namespace)

    self._classes = None
    self._classes_map = {}
    #
    # Pick up the list of classes under this namespace
    #  so that they can be queried, and used as though
    #  properties of the namespace by means of the __getattr__
    #  hook below.
    # If the namespace does not support SubclassesOf, carry on
    #  regardless
    #
    if find_classes:
      _ = self.classes

  def __repr__ (self):
    return "<_wmi_namespace: %s>" % self.wmi

  def __str__ (self):
    return repr (self)

  def _get_classes (self):
    if self._classes is None:
      self._classes = self.subclasses_of ()
    return SelfDeprecatingDict (dict.fromkeys (self._classes))
  classes = property (_get_classes)

  def get (self, moniker):
    try:
      return _wmi_object (self.wmi.Get (moniker))
    except pywintypes.com_error:
      handle_com_error ()

  def handle (self):
    """The raw OLE object representing the WMI namespace"""
    return self._namespace

  def subclasses_of (self, root="", regex=r".*"):
    try:
      SubclassesOf = self._namespace.SubclassesOf
    except AttributeError:
      return set ()
    else:
      return set (
        c.Path_.Class
          for c in SubclassesOf (root)
          if re.match (regex, c.Path_.Class)
      )

  def instances (self, class_name):
    """Return a list of instances of the WMI class. This is
    (probably) equivalent to querying with no qualifiers::

      wmi.WMI ().instances ("Win32_LogicalDisk")
      # should be the same as
      wmi.WMI ().Win32_LogicalDisk ()
    """
    try:
      return [_wmi_object (obj) for obj in self._namespace.InstancesOf (class_name)]
    except pywintypes.com_error:
      handle_com_error ()

  def new (self, wmi_class, **kwargs):
    """This is now implemented by a call to :meth:`_wmi_class.new`"""
    return getattr (self, wmi_class).new (**kwargs)

  new_instance_of = new

  def _raw_query (self, wql):
    """Execute a WQL query and return its raw results.  Use the flags
    recommended by Microsoft to achieve a read-only, semi-synchronous
    query where the time is taken while looping through.
    NB Backslashes need to be doubled up.
    """
    flags = wbemFlagReturnImmediately | wbemFlagForwardOnly
    wql = wql.replace ("\\", "\\\\")
    try:
      return self._namespace.ExecQuery (strQuery=wql, iFlags=flags)
    except pywintypes.com_error:
      handle_com_error ()

  def query (self, wql, instance_of=None, fields=[]):
    """Perform an arbitrary query against a WMI object, and return
    a list of _wmi_object representations of the results.
    """
    return [ _wmi_object (obj, instance_of, fields) for obj in self._raw_query(wql) ]

  def fetch_as_classes (self, wmi_classname, fields=(), **where_clause):
    """Build and execute a wql query to fetch the specified list of fields from
    the specified wmi_classname + where_clause, then return the results as
    a list of simple class instances with attributes matching field_list.

    If fields is left empty, select * and pre-load all class attributes for
    each class returned.
    """
    wql = "SELECT %s FROM %s" % (fields and ", ".join (fields) or "*", wmi_classname)
    if where_clause:
      wql += " WHERE " + " AND ".join (["%s = '%s'" % (k, v) for k, v in where_clause.items()])
    return [_wmi_result (obj, fields) for obj in self._raw_query(wql)]

  def fetch_as_lists (self, wmi_classname, fields, **where_clause):
    """Build and execute a wql query to fetch the specified list of fields from
    the specified wmi_classname + where_clause, then return the results as
    a list of lists whose values correspond to field_list.
    """
    wql = "SELECT %s FROM %s" % (", ".join (fields), wmi_classname)
    if where_clause:
      wql += " WHERE " + " AND ".join (["%s = '%s'" % (k, v) for k, v in where_clause.items()])
    results = []
    for obj in self._raw_query(wql):
        results.append ([obj.Properties_ (field).Value for field in fields])
    return results

  def watch_for (
    self,
    raw_wql=None,
    notification_type="operation",
    wmi_class=None,
    delay_secs=1,
    fields=[],
    **where_clause
  ):
    """Set up an event tracker on a WMI event. This function
    returns an wmi_watcher which can be called to get the
    next event::

      c = wmi.WMI ()

      raw_wql = "SELECT * FROM __InstanceCreationEvent WITHIN 2 WHERE TargetInstance ISA 'Win32_Process'"
      watcher = c.watch_for (raw_wql=raw_wql)
      while 1:
        process_created = watcher ()
        print(process_created.Name)

      # or

      watcher = c.watch_for (
        notification_type="Creation",
        wmi_class="Win32_Process",
        delay_secs=2,
        Name='calc.exe'
      )
      calc_created = watcher ()

    Now supports timeout on the call to watcher::

      import pythoncom
      import wmi
      c = wmi.WMI (privileges=["Security"])
      watcher1 = c.watch_for (
        notification_type="Creation",
        wmi_class="Win32_NTLogEvent",
        Type="error"
      )
      watcher2 = c.watch_for (
        notification_type="Creation",
        wmi_class="Win32_NTLogEvent",
        Type="warning"
      )

      while 1:
        try:
          error_log = watcher1 (500)
        except wmi.x_wmi_timed_out:
          pythoncom.PumpWaitingMessages ()
        else:
          print(error_log)

        try:
          warning_log = watcher2 (500)
        except wmi.x_wmi_timed_out:
          pythoncom.PumpWaitingMessages ()
        else:
          print(warning_log)
    """
    if isinstance (wmi_class, _wmi_class):
      class_name = wmi_class._class_name
    else:
      class_name = wmi_class
      wmi_class = getattr (self, class_name)
    is_extrinsic = "__ExtrinsicEvent" in wmi_class.derivation ()
    if raw_wql:
      wql = raw_wql
    else:
      fields = set (['TargetInstance'] + (fields or ["*"]))
      field_list = ", ".join (fields)
      if is_extrinsic:
        if where_clause:
          where = " WHERE " + " AND ".join (["%s = '%s'" % (k, v) for k, v in where_clause.items ()])
        else:
          where = ""
        wql = "SELECT " + field_list + " FROM " + class_name + where
      else:
        if where_clause:
          where = " AND " + " AND ".join (["TargetInstance.%s = '%s'" % (k, v) for k, v in where_clause.items ()])
        else:
          where = ""
        wql = \
          "SELECT %s FROM __Instance%sEvent WITHIN %d WHERE TargetInstance ISA '%s' %s" % \
          (field_list, notification_type, delay_secs, class_name, where)

    try:
      return _wmi_watcher (
        self._namespace.ExecNotificationQuery (wql),
        is_extrinsic=is_extrinsic,
        fields=fields
      )
    except pywintypes.com_error:
      handle_com_error ()

  def __getattr__ (self, attribute):
    """Offer WMI classes as simple attributes. Pass through any untrapped
    unattribute to the underlying OLE object. This means that new or
    unmapped functionality is still available to the module user.
    """
    #
    # Don't try to match against known classes as was previously
    # done since the list may not have been requested
    # (find_classes=False).
    #
    try:
      return self._cached_classes (attribute)
    except pywintypes.com_error:
      return getattr (self._namespace, attribute)

  def _cached_classes (self, class_name):
    """Standard caching helper which keeps track of classes
    already retrieved by name and returns the existing object
    if found. If this is the first retrieval, store it and
    pass it back
    """
    if class_name not in self._classes_map:
      self._classes_map[class_name] = _wmi_class (self, self._namespace.Get (class_name))
    return self._classes_map[class_name]

  def _getAttributeNames (self):
    """Return list of classes for IPython completion engine"""
    return [x for x in self.classes if not x.startswith ('__')]

#
# class _wmi_watcher
#
class _wmi_watcher:
  """Helper class for WMI.watch_for below (qv)"""

  _event_property_map = {
    "TargetInstance" : _wmi_object,
    "PreviousInstance" : _wmi_object
  }
  def __init__ (self, wmi_event, is_extrinsic, fields=[]):
    self.wmi_event = wmi_event
    self.is_extrinsic = is_extrinsic
    self.fields = fields

  def __call__ (self, timeout_ms=-1):
    """When called, return the instance which caused the event. Supports
     timeout in milliseconds (defaulting to infinite). If the watcher
     times out, :exc:`x_wmi_timed_out` is raised. This makes it easy to support
     watching for multiple objects.
    """
    try:
      event = self.wmi_event.NextEvent (timeout_ms)
      if self.is_extrinsic:
        return _wmi_event (event, None, self.fields)
      else:
        return _wmi_event (
          event.Properties_ ("TargetInstance").Value,
          _wmi_object (event, property_map=self._event_property_map),
          self.fields
        )
    except pywintypes.com_error:
      handle_com_error ()

PROTOCOL = "winmgmts:"
def connect (
  computer="",
  impersonation_level="",
  authentication_level="",
  authority="",
  privileges="",
  moniker="",
  wmi=None,
  namespace="",
  suffix="",
  user="",
  password="",
  find_classes=False,
  debug=False
):
  """The WMI constructor can either take a ready-made moniker or as many
  parts of one as are necessary. Eg::

    c = wmi.WMI (moniker="winmgmts:{impersonationLevel=Delegate}//remote")
    # or
    c = wmi.WMI (computer="remote", privileges=["!RemoteShutdown", "Security"])

  I daren't link to a Microsoft URL; they change so often. Try Googling for
  WMI construct moniker and see what it comes back with.

  For complete control, a named argument "wmi" can be supplied, which
  should be a SWbemServices object, which you create yourself. Eg::

    loc = win32com.client.Dispatch("WbemScripting.SWbemLocator")
    svc = loc.ConnectServer(...)
    c = wmi.WMI(wmi=svc)

  This is the only way of connecting to a remote computer with a different
  username, as the moniker syntax does not allow specification of a user
  name.

  If the `wmi` parameter is supplied, all other parameters are ignored.
  """
  global _DEBUG
  _DEBUG = debug

  try:
    try:
      if wmi:
        obj = wmi

      elif moniker:
        if not moniker.startswith (PROTOCOL):
          moniker = PROTOCOL + moniker
        obj = GetObject (moniker)

      else:
        if user:
          if privileges or suffix:
            raise x_wmi_authentication ("You can't specify privileges or a suffix as well as a username")
          elif computer in (None, '', '.'):
            raise x_wmi_authentication ("You can only specify user/password for a remote connection")
          else:
            obj = connect_server (
              server=computer,
              namespace=namespace,
              user=user,
              password=password,
              authority=authority,
              impersonation_level=impersonation_level,
              authentication_level=authentication_level
            )

        else:
          moniker = construct_moniker (
            computer=computer,
            impersonation_level=impersonation_level,
            authentication_level=authentication_level,
            authority=authority,
            privileges=privileges,
            namespace=namespace,
            suffix=suffix
          )
          obj = GetObject (moniker)

      wmi_type = get_wmi_type (obj)

      if wmi_type == "namespace":
        return _wmi_namespace (obj, find_classes)
      elif wmi_type == "class":
        return _wmi_class (None, obj)
      elif wmi_type == "instance":
        return _wmi_object (obj)
      else:
        raise x_wmi ("Unknown moniker type")

    except pywintypes.com_error:
      handle_com_error ()

  except x_wmi_uninitialised_thread:
    raise x_wmi_uninitialised_thread ("WMI returned a syntax error: you're probably running inside a thread without first calling pythoncom.CoInitialize[Ex]")

WMI = connect

def construct_moniker (
  computer=None,
  impersonation_level=None,
  authentication_level=None,
  authority=None,
  privileges=None,
  namespace=None,
  suffix=None
):
  security = []
  if impersonation_level: security.append ("impersonationLevel=%s" % impersonation_level)
  if authentication_level: security.append ("authenticationLevel=%s" % authentication_level)
  #
  # Use of the authority descriptor is invalid on the local machine
  #
  if authority and computer: security.append ("authority=%s" % authority)
  if privileges: security.append ("(%s)" % ", ".join (privileges))

  moniker = [PROTOCOL]
  if security: moniker.append ("{%s}!" % ",".join (security))
  if computer: moniker.append ("//%s/" % computer)
  if namespace:
    parts = re.split (r"[/\\]", namespace)
    if parts[0] != 'root':
      parts.insert (0, "root")
    moniker.append ("/".join (parts))
  if suffix: moniker.append (":%s" % suffix)
  return "".join (moniker)

def get_wmi_type (obj):
  try:
    path = obj.Path_
  except AttributeError:
    return "namespace"
  else:
    if path.IsClass:
      return "class"
    else:
      return "instance"

def connect_server (
  server,
  namespace = "",
  user = "",
  password = "",
  locale = "",
  authority = "",
  impersonation_level="",
  authentication_level="",
  security_flags = 0x80,
  named_value_set = None
):
  """Return a remote server running WMI

  :param server: name of the server
  :param namespace: namespace to connect to - defaults to whatever's defined as default
  :param user: username to connect as, either local or domain (dom\\name or user@domain for XP)
  :param password: leave blank to use current context
  :param locale: desired locale in form MS_XXXX (eg MS_409 for Am En)
  :param authority: either "Kerberos:" or an NT domain. Not needed if included in user
  :param impersonation_level: valid WMI impersonation level
  :param security_flags: if 0, connect will wait forever; if 0x80, connect will timeout at 2 mins
  :param named_value_set: typically empty, otherwise a context-specific `SWbemNamedValueSet`

  Example::

    remote_connetion = wmi.connect_server (
      server="remote_machine", user="myname", password="mypassword"
    )
    c = wmi.WMI (wmi=remote_connection)
  """
  #
  # Thanks to Matt Mercer for example code to set
  # impersonation & authentication on ConnectServer
  #
  if impersonation_level:
    try:
      impersonation = getattr (obj._constants, "wbemImpersonationLevel%s" % impersonation_level.title ())
    except AttributeError:
      raise x_wmi_authentication ("No such impersonation level: %s" % impersonation_level)
  else:
    impersonation = None

  if authentication_level:
    try:
      authentication = getattr (obj._constants, "wbemAuthenticationLevel%s" % authentication_level.title ())
    except AttributeError:
      raise x_wmi_authentication ("No such impersonation level: %s" % impersonation_level)
  else:
    authentication = None

  server = Dispatch ("WbemScripting.SWbemLocator").\
    ConnectServer (
      server,
      namespace,
      user,
      password,
      locale,
      authority,
      security_flags,
      named_value_set
    )
  if impersonation:
    server.Security_.ImpersonationLevel  = impersonation
  if authentication:
    server.Security_.AuthenticationLevel  = authentication
  return server

def Registry (
  computer=None,
  impersonation_level="Impersonate",
  authentication_level="Default",
  authority=None,
  privileges=None,
  moniker=None
):

  warnings.warn ("This function can be implemented using wmi.WMI (namespace='DEFAULT').StdRegProv", DeprecationWarning)
  if not moniker:
    moniker = construct_moniker (
      computer=computer,
      impersonation_level=impersonation_level,
      authentication_level=authentication_level,
      authority=authority,
      privileges=privileges,
      namespace="default",
      suffix="StdRegProv"
    )

  try:
    return _wmi_object (GetObject (moniker))

  except pywintypes.com_error:
    handle_com_error ()

#
# Typical use test
#
if __name__ == '__main__':
  system = WMI ()
  for my_computer in system.Win32_ComputerSystem ():
    print("Disks on", my_computer.Name)
    for disk in system.Win32_LogicalDisk ():
      print(disk.Caption, disk.Description, disk.ProviderName or "")

  0707010005ca18000081b40000000000000000000000015ab217540000b6c2000000660000000000000000000000000000002b00000000root/usr/share/opensvc/lib/xmlrpcClient.py    from __future__ import print_function
import socket
import sys
import os
socket.setdefaulttimeout(180)

kwargs = {}
try:
    import ssl
    kwargs["context"] = ssl._create_unverified_context()
    kwargs["allow_none"] = True
except:
    pass

try:
    import xmlrpclib
except ImportError:
    import xmlrpc.client as xmlrpclib

try:
    import httplib
except ImportError:
    import http.client as httplib

def get_proxy(uri):
    try:
        return xmlrpclib.ServerProxy(uri, **kwargs)
    except Exception as e:
        if "__init__" in str(e):
            return xmlrpclib.ServerProxy(uri)

from datetime import datetime, timedelta
import time
import random
import os
import sys
from rcGlobalEnv import rcEnv
import rcStatus
import rcExceptions as ex

rcEnv.warned = False

import logging
import logging.handlers
logfile = os.path.join(rcEnv.paths.pathlog, 'xmlrpc.log')
log = logging.getLogger("xmlrpc")
log.setLevel(logging.INFO)

try:
    fileformatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
    filehandler = logging.handlers.RotatingFileHandler(os.path.join(logfile),
                                                       maxBytes=5242880,
                                                       backupCount=5)
    filehandler.setFormatter(fileformatter)
    log.addHandler(filehandler)
except Exception as e:
    pass

def do_call(fn, args, kwargs, log, proxy, mode="synchronous"):
    tries = 5
    for i in range(tries):
        try:
            return _do_call(fn, args, kwargs, log, proxy, mode=mode)
        except Exception as e:
            s = str(e)
            if "retry" in s:
                # db table changed. retry immediately
                max_wait = 0
            elif "restart" in s or "Gateway" in s:
                # collector overload issues, retry after a random delay
                max_wait = 3
            else:
                # no need to retry at all there, unknown cause
                raise
        if max_wait > 0:
            time.sleep(random.random()*max_wait)
        log.warning("retry call %s on error %s" % (fn, str(e)))
    log.error("failed to call %s after %d tries" % (fn, tries))

def _do_call(fn, args, kwargs, log, proxy, mode="synchronous"):
    log.info("call remote function %s in %s mode"%(fn, mode))
    try:
        _b = datetime.now()
        buff = getattr(proxy, fn)(*args, **kwargs)
        _e = datetime.now()
        _d = _e - _b
        log.info("call %s done in %d.%03d seconds"%(fn, _d.seconds, _d.microseconds//1000))
        return buff
    except Exception as e:
        _e = datetime.now()
        _d = _e - _b
        log.exception("call %s error after %d.%03d seconds"%(fn, _d.seconds, _d.microseconds//1000))

class Collector(object):
    def call(self, *args, **kwargs):
        fn = args[0]
        self.init(fn)
        if rcEnv.dbopensvc is None:
            return {"ret": 1, "msg": "no collector defined. set 'dbopensvc' in node.conf"}
        if len(self.proxy_methods) == 0:
            return
        if len(args) > 1:
            args = args[1:]
        else:
            args = []
        if fn == "register_node" and \
           'register_node' not in self.proxy_methods:
            print("collector does not support node registration", file=sys.stderr)
            return
        if rcEnv.uuid == "" and \
           rcEnv.dbopensvc is not None and \
           not rcEnv.warned and \
           self.auth_node and \
           fn != "register_node":
            print("this node is not registered. try 'nodemgr register'", file=sys.stderr)
            print("to disable this warning, set 'dbopensvc = None' in node.conf", file=sys.stderr)
            rcEnv.warned = True
            return
        return do_call(fn, args, kwargs, self.log, self, mode="synchronous")

    def __init__(self, node=None):
        self.node = node
        self.proxy = None
        self.proxy_methods = []
        self.comp_proxy = None
        self.comp_proxy_methods = []

        self.comp_fns = ['comp_get_data_moduleset',
                         'comp_get_svc_data_moduleset',
                         'comp_get_data',
                         'comp_get_svc_data',
                         'comp_attach_moduleset',
                         'comp_attach_svc_moduleset',
                         'comp_detach_moduleset',
                         'comp_detach_svc_moduleset',
                         'comp_get_ruleset',
                         'comp_get_svc_ruleset',
                         'comp_get_ruleset_md5',
                         'comp_attach_ruleset',
                         'comp_attach_svc_ruleset',
                         'comp_detach_ruleset',
                         'comp_detach_svc_ruleset',
                         'comp_list_ruleset',
                         'comp_list_moduleset',
                         'comp_show_status',
                         'comp_log_actions']
        self.auth_node = True
        self.log = logging.getLogger("xmlrpc")

    def get_methods_dbopensvc(self):
        if rcEnv.dbopensvc is None:
            self.proxy_methods = []
            return
        self.log.debug("get dbopensvc method list")
        try:
            if self.proxy is None:
                self.proxy = get_proxy(rcEnv.dbopensvc)
            self.proxy_methods = self.proxy.system.listMethods()
        except Exception as e:
            self.log.error(str(e))
            self.proxy = get_proxy("https://127.0.0.1/")
            self.proxy_methods = []
        self.log.debug("%d feed methods"%len(self.proxy_methods))

    def get_methods_dbcompliance(self):
        if rcEnv.dbcompliance is None:
            self.comp_proxy_methods = []
            return
        self.log.debug("get dbcompliance method list")
        try:
            if self.comp_proxy is None:
                self.comp_proxy = get_proxy(rcEnv.dbcompliance)
            self.comp_proxy_methods = self.comp_proxy.system.listMethods()
        except Exception as e:
            self.log.error(str(e))
            self.comp_proxy = get_proxy("https://127.0.0.1/")
            self.comp_proxy_methods = []
        self.log.debug("%d compliance methods"%len(self.comp_proxy_methods))

    def init(self, fn=None):
        if fn is not None:
            if fn in self.comp_fns:
                if self.comp_proxy is not None:
                    return
            elif self.proxy is not None:
                return

        if rcEnv.dbopensvc is None:
            return

        try:
            a = socket.getaddrinfo(rcEnv.dbopensvc_host, None)
            if len(a) == 0:
                raise Exception
            dbopensvc_ip = a[0][-1][0]
        except:
            self.log.error("could not resolve %s to an ip address. disable collector updates."%rcEnv.dbopensvc_host)

        try:
            a = socket.getaddrinfo(rcEnv.dbcompliance_host, None)
            if len(a) == 0:
                raise Exception
            dbcompliance_ip = a[0][-1][0]
        except Exception as e:
            self.log.error(str(e))
            self.log.error("could not resolve %s to an ip address. disable collector updates."%rcEnv.dbcompliance_host)

        try:
            self.proxy = get_proxy(rcEnv.dbopensvc)
            self.get_methods_dbopensvc()
        except Exception as e:
            self.log.error(str(e))
            self.proxy = get_proxy("https://127.0.0.1/")

        if fn in self.comp_fns:
            try:
                self.comp_proxy = get_proxy(rcEnv.dbcompliance)
                self.get_methods_dbcompliance()
            except:
                self.comp_proxy = get_proxy("https://127.0.0.1/")

        self.log.info("feed proxy %s"%str(self.proxy))
        self.log.info("compliance proxy %s"%str(self.comp_proxy))

        if "register_node" not in self.proxy_methods:
            self.auth_node = False

    def begin_action(self, svcname, action, version, begin, cron):
        args = [['svcname',
             'action',
             'hostname',
             'version',
             'begin',
             'cron'],
            [str(svcname),
             str(action),
             str(rcEnv.nodename),
             str(version),
             str(begin),
             '1' if cron else '0']
        ]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        self.proxy.begin_action(*args)

    def end_action(self, svcname, action, begin, end, cron, alogfile):
        err = 'ok'
        res = None
        res_err = None
        pid = None
        msg = None
        with open(alogfile, 'r') as ofile:
            lines = ofile.read()
        try:
            os.unlink(alogfile)
        except Exception:
            pass
        pids = set()

        """Example logfile line:
        2009-11-11 01:03:25,252;;DISK.VG;;INFO;;unxtstsvc01_data is already up;;10200;;EOL
        """
        vars = ['svcname',
                'action',
                'hostname',
                'pid',
                'begin',
                'end',
                'status_log',
                'status',
                'cron']
        vals = []
        last = None
        for line in lines.split(';;EOL\n'):
            if line.count(';;') != 4:
                continue
            if ";;status_history;;" in line:
                continue
            date = line.split(';;')[0]

            res_err = 'ok'
            date, res, lvl, msg, pid = line.split(';;')
            res = res.lower().replace(rcEnv.nodename+'.'+svcname,'').replace(rcEnv.nodename, '').lstrip(".")
            res_action = res + ' ' + action
            res_action = res_action.strip()
            date = date.split(",")[0]

            # database overflow protection
            trim_lim = 10000
            trim_tag = ' <trimmed> '
            trim_head = trim_lim // 2
            trim_tail = trim_head-len(trim_tag)
            if len(msg) > trim_lim:
                msg = msg[:trim_head]+' <trimmed> '+msg[-trim_tail:]

            pids |= set([pid])
            if lvl is None or lvl == 'DEBUG':
                continue
            elif lvl == 'ERROR':
                err = 'err'
                res_err = 'err'
            elif lvl == 'WARNING' and err != 'err':
                err = 'warn'
            elif lvl == 'WARNING' and res_err != 'err':
                res_err = 'warn'

            try:
                if last:
                    if last[3] == pid and last[1] == res_action and last[7] == res_err:
                        last[6] += "\n"+msg
                        continue
                    else:
                        vals.append(last)
            except Exception as exc:
                print(exc)
                continue
            
            last = [
                svcname,
                res_action,
                rcEnv.nodename,
                pid,
                date,
                "",
                msg,
                res_err,
                '1' if cron else '0'
            ]

        if last:
            vals.append(last)

        if len(vals) > 0:
            args = [vars, vals]
            if self.auth_node:
                args += [(rcEnv.uuid, rcEnv.nodename)]
            self.proxy.res_action_batch(*args)

        """Complete the wrap-up database entry
        """

        """ If logfile is empty, default to current process pid
        """
        if len(pids) == 0:
            pids = set([os.getpid()])

        duration = datetime.strptime(end, "%Y-%m-%d %H:%M:%S") - \
                   datetime.strptime(begin, "%Y-%m-%d %H:%M:%S")
        args = [
            ['svcname',
             'action',
             'hostname',
             'pid',
             'begin',
             'end',
             'time',
             'status',
             'cron'],
            [str(svcname),
             str(action),
             str(rcEnv.nodename),
             ','.join(map(str, pids)),
             begin,
             end,
             str(duration.seconds),
             str(err),
             '1' if cron else '0']
        ]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        self.proxy.end_action(*args)

    def svcmon_update_combo(self, g_vars, g_vals, r_vars, r_vals):
        if 'svcmon_update_combo' in self.proxy_methods:
            args = [g_vars, g_vals, r_vars, r_vals]
            if self.auth_node:
                args += [(rcEnv.uuid, rcEnv.nodename)]
            self.proxy.svcmon_update_combo(*args)
        else:
            args = [g_vars, g_vals]
            if self.auth_node:
                args += [(rcEnv.uuid, rcEnv.nodename)]
            self.proxy.svcmon_update(*args)
            args = [r_vars, r_vals]
            if self.auth_node:
                args += [(rcEnv.uuid, rcEnv.nodename)]
            self.proxy.resmon_update(*args)

    def push_resinfo(self, vals, sync=True):
        if 'update_resinfo' not in self.proxy_methods:
            return
        vars = ['res_svcname',
                'res_nodename',
                'topology',
                'rid',
                'res_key',
                'res_value']
        if len(vals) == 0:
            return
        args = [vars, vals]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        self.proxy.update_resinfo(*args)

    def push_config(self, svc, sync=True):
        def repr_config(svc):
            import codecs
            if not os.path.exists(svc.paths.cf):
                return
            with codecs.open(svc.paths.cf, 'r', encoding="utf8") as f:
                buff = f.read()
                return buff
            return

        vars = ['svc_name',
                'svc_topology',
                'svc_flex_min_nodes',
                'svc_flex_max_nodes',
                'svc_flex_cpu_low_threshold',
                'svc_flex_cpu_high_threshold',
                'svc_env',
                'svc_nodes',
                'svc_drpnode',
                'svc_drpnodes',
                'svc_comment',
                'svc_app',
                'svc_config',
                'svc_ha']

        vals = [svc.svcname,
                svc.topology,
                svc.flex_min_nodes,
                svc.flex_max_nodes,
                svc.flex_cpu_low_threshold,
                svc.flex_cpu_high_threshold,
                svc.svc_env,
                ' '.join(svc.nodes),
                svc.drpnode,
                ' '.join(svc.drpnodes),
                svc.comment,
                svc.app,
                repr_config(svc),
                '1' if svc.ha else '0']

        args = [vars, vals]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        self.proxy.update_service(*args)

    def push_containerinfo(self, svc, sync=True):
        vars = ['mon_svcname',
                'mon_nodname',
                'mon_vmname',
                'mon_guestos',
                'mon_vmem',
                'mon_vcpus',
                'mon_containerpath']
        vals = []

        for container in svc.get_resources('container'):
            container_info = container.get_container_info()
            vals += [[svc.svcname,
                      rcEnv.nodename,
                      container.vm_hostname,
                      container.guestos if hasattr(container, 'guestos') and container.guestos is not None else "",
                      container_info['vmem'],
                      container_info['vcpus'],
                      container.zonepath if hasattr(container, 'zonepath') else ""]]

        if len(vals) > 0:
            args = [vars, vals]
            if self.auth_node:
                args += [(rcEnv.uuid, rcEnv.nodename)]
            self.proxy.svcmon_update(*args)

    def push_disks(self, data, sync=True):
        vars = ['disk_id',
                'disk_svcname',
                'disk_size',
                'disk_used',
                'disk_vendor',
                'disk_model',
                'disk_dg',
                'disk_nodename',
                'disk_region']
        vals = []
        served_disks = []

        for disk_id, disk in data["disks"].items():
            for svcname, service in disk["services"].items():
                vals.append([
                 disk_id,
                 svcname,
                 disk["size"],
                 service["used"],
                 disk["vendor"],
                 disk["model"],
                 service["dg"],
                 rcEnv.nodename,
                 service["region"],
            ])
            if disk["used"] < disk["size"]:
                vals.append([
                 disk_id,
                 "",
                 disk["size"],
                 disk["size"] - disk["used"],
                 disk["vendor"],
                 disk["model"],
                 "",
                 rcEnv.nodename,
                 0,
            ])

        args = [vars, vals]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        self.proxy.register_disks(*args)

        #
        # register disks this node provides to its VM
        #
        vars = ['disk_id',
                'disk_arrayid',
                'disk_devid',
                'disk_size',
                'disk_raid',
                'disk_group']
        vals = []

        for disk_id, disk in data["served_disks"].items():
            vals.append([
              disk["vdisk_id"],
              disk["cluster"],
              disk_id,
              disk["size"],
              "virtual",
              "virtual"
            ])

        args = [vars, vals]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        self.proxy.register_diskinfo(*args)

    def push_stats_fs_u(self, l, sync=True):
        args = [l[0], l[1]]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        self.proxy.insert_stats_fs_u(*args)

    def push_pkg(self, sync=True):
        p = __import__('rcPkg'+rcEnv.sysname)
        vars = ['pkg_nodename',
                'pkg_name',
                'pkg_version',
                'pkg_arch']
        vals = p.listpkg()
        n = len(vals)
        if n == 0:
            print("No package found. Skip push.")
            return
        else:
            print("Pushing %d packages information."%n)
        if len(vals[0]) >= 5:
            vars.append('pkg_type')
        if len(vals[0]) >= 6:
            vars.append('pkg_install_date')
        if len(vals[0]) >= 7:
            vars.append('pkg_sig')
        args = [rcEnv.nodename]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        args = [vars, vals]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        self.proxy.insert_pkg(*args)

    def push_patch(self, sync=True):
        p = __import__('rcPkg'+rcEnv.sysname)
        vars = ['patch_nodename',
                'patch_num',
                'patch_rev']
        vals = p.listpatch()
        if len(vals) == 0:
            return
        if len(vals[0]) == 4:
            vars.append('patch_install_date')
        args = [rcEnv.nodename]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        args = [vars, vals]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        self.proxy.insert_patch(*args)

    def push_stats(self, interval=None, stats_dir=None,
                   stats_start=None, stats_end=None, sync=True, disable=None):
        try:
            s = __import__('rcStats'+rcEnv.sysname)
        except ImportError:
            return

        try:
            sp = s.StatsProvider(interval=interval,
                                 stats_dir=stats_dir,
                                 stats_start=stats_start,
                                 stats_end=stats_end)
        except ValueError as e:
            print(str(e))
            return 1
        except Exception as e:
            print(e)
            raise
        h = {}
        for stat in ['cpu', 'mem_u', 'proc', 'swap', 'block',
                     'blockdev', 'netdev', 'netdev_err', 'svc', 'fs_u']:
            if disable is not None and stat in disable:
                print("%s collection is disabled in node configuration"%stat)
                continue
            h[stat] = sp.get(stat)
            print("%s stats: %d samples" % (stat, len(h[stat][1])))
        import json
        args = [json.dumps(h)]
        if self.auth_node:
             args += [(rcEnv.uuid, rcEnv.nodename)]
        print("pushing")
        self.proxy.insert_stats(*args)

    def sysreport_lstree(self, sync=True):
        args = []
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        data = self.proxy.sysreport_lstree(*args)
        return data

    def send_sysreport(self, fpath, deleted, sync=True):
        args = []
        if fpath is None:
            args += ["", ""]
        else:
            with open(fpath, 'rb') as f:
                binary = xmlrpclib.Binary(f.read())
            args = [os.path.basename(fpath), binary]
            print("archive length:", len(binary.data))
        args += [deleted]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        self.proxy.send_sysreport(*args)

    def push_asset(self, node, data=None, sync=True):
        if "update_asset" not in self.proxy_methods:
            print("'update_asset' method is not exported by the collector")
            return
        d = dict(data)

        gen = {}
        if 'hardware' in d:
            gen["hardware"] = d["hardware"]
            del(d['hardware'])

        if 'hba' in d:
            vars = ['nodename', 'hba_id', 'hba_type']
            vals = [(rcEnv.nodename, _d["hba_id"], _d["hba_type"]) for _d in d['hba']]
            del(d['hba'])
            gen.update({'hba': [vars, vals]})

        if 'targets' in d:
            vars = ['hba_id', 'tgt_id']
            vals = [(_d["hba_id"], _d["tgt_id"]) for _d in d['targets']]
            del(d['targets'])
            gen.update({'targets': [vars, vals]})

        if 'lan' in d:
            vars = ['mac', 'intf', 'type', 'addr', 'mask', 'flag_deprecated']
            vals = []
            for mac, l in d['lan'].items():
                for _d in l:
                    vals.append([mac, _d['intf'], _d['type'], _d['addr'], _d['mask'], _d['flag_deprecated']])
            del(d['lan'])
            gen.update({'lan': [vars, vals]})

        if 'uids' in d:
            vars = ['user_name', 'user_id']
            vals = [(_d["username"], _d["uid"]) for _d in d['uids']]
            del(d['uids'])
            gen.update({'uids': [vars, vals]})

        if 'gids' in d:
            vars = ['group_name', 'group_id']
            vals = [(_d["groupname"], _d["gid"]) for _d in d['gids']]
            del(d['gids'])
            gen.update({'gids': [vars, vals]})

        if len(gen) > 0:
            args = [gen]
            if self.auth_node:
                args += [(rcEnv.uuid, rcEnv.nodename)]
            self.proxy.insert_generic(*args)

        _vars = []
        _vals = []
        for key, _d in d.items():
            _vars.append(key)
            if _d["value"] is None:
                _d["value"] = ""
            _vals.append(_d["value"])
        args = [_vars, _vals]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        if node.options.syncrpc:
            self.proxy.update_asset_sync(*args)
        else:
            self.proxy.update_asset(*args)

    def daemon_ping(self, sync=True):
        args = [(rcEnv.uuid, rcEnv.nodename)]
        self.proxy.daemon_ping(*args)

    def push_daemon_status(self, data, changes=None, sync=True):
        import json
        args = [json.dumps(data), json.dumps(changes), (rcEnv.uuid, rcEnv.nodename)]
        self.proxy.push_daemon_status(*args)

    def push_brocade(self, objects=[], sync=True):
        if 'update_brocade' not in self.proxy_methods:
            print("'update_brocade' method is not exported by the collector")
            return
        m = __import__('rcBrocade')
        try:
            brocades = m.Brocades(objects)
        except:
            return
        for brocade in brocades:
            vals = []
            for key in brocade.keys:
                try:
                    vals.append(getattr(brocade, 'get_'+key)())
                except:
                    print("error fetching", key)
                    continue
            args = [brocade.name, brocade.keys, vals]
            if self.auth_node:
                args += [(rcEnv.uuid, rcEnv.nodename)]
            self.proxy.update_brocade(*args)

    def push_vioserver(self, objects=[], sync=True):
        if 'update_vioserver' not in self.proxy_methods:
            print("'update_vioserver' method is not exported by the collector")
            return
        m = __import__('rcVioServer')
        try:
            vioservers = m.VioServers(objects)
        except:
            return
        for vioserver in vioservers:
            vals = []
            for key in vioserver.keys:
                vals.append(getattr(vioserver, 'get_'+key)())
            args = [vioserver.name, vioserver.keys, vals]
            if self.auth_node:
                args += [(rcEnv.uuid, rcEnv.nodename)]
            self.proxy.update_vioserver(*args)

    def push_hds(self, objects=[], sync=True):
        if 'update_hds' not in self.proxy_methods:
            print("'update_hds' method is not exported by the collector")
            return
        m = __import__('rcHds')
        try:
            hdss = m.Arrays(objects)
        except Exception as e:
            print(e, file=sys.stderr)
            return
        for hds in hdss:
            vals = []
            for key in hds.keys:
                vals.append(getattr(hds, 'get_'+key)())
            args = [hds.name, hds.keys, vals]
            if self.auth_node:
                args += [(rcEnv.uuid, rcEnv.nodename)]
            self.proxy.update_hds(*args)

    def push_necism(self, objects=[], sync=True):
        if 'update_necism' not in self.proxy_methods:
            print("'update_necism' method is not exported by the collector")
            return
        m = __import__('rcNecIsm')
        try:
            necisms = m.NecIsms(objects)
        except:
            return
        for necism in necisms:
            vals = []
            for key in necism.keys:
                vals.append(getattr(necism, 'get_'+key)())
            args = [necism.name, necism.keys, vals]
            if self.auth_node:
                args += [(rcEnv.uuid, rcEnv.nodename)]
            self.proxy.update_necism(*args)

    def push_hp3par(self, objects=[], sync=True):
        if 'update_hp3par' not in self.proxy_methods:
            print("'update_hp3par' method is not exported by the collector")
            return
        m = __import__('rcHp3par')
        try:
            hp3pars = m.Hp3pars(objects)
        except:
            return
        for hp3par in hp3pars:
            vals = []
            for key in hp3par.keys:
                vals.append(getattr(hp3par, 'get_'+key)())
            args = [hp3par.name, hp3par.keys, vals]
            if self.auth_node:
                args += [(rcEnv.uuid, rcEnv.nodename)]
            self.proxy.update_hp3par(*args)

    def push_centera(self, objects=[], sync=True):
        if 'update_centera' not in self.proxy_methods:
            print("'update_centera' method is not exported by the collector")
            return
        m = __import__('rcCentera')
        try:
            centeras = m.Centeras(objects)
        except:
            return
        for centera in centeras:
            vals = []
            print(centera.name)
            for key in centera.keys:
                print(" extract", key)
                vals.append(getattr(centera, 'get_'+key)())
            args = [centera.name, [k+".xml" for k in centera.keys], vals]
            if self.auth_node:
                args += [(rcEnv.uuid, rcEnv.nodename)]
            self.proxy.update_centera(*args)

    def push_emcvnx(self, objects=[], sync=True):
        if 'update_emcvnx' not in self.proxy_methods:
            print("'update_emcvnx' method is not exported by the collector")
            return
        m = __import__('rcEmcVnx')
        try:
            emcvnxs = m.EmcVnxs(objects)
        except:
            return
        for emcvnx in emcvnxs:
            vals = []
            print(emcvnx.name)
            for key in emcvnx.keys:
                print(" extract", key)
                vals.append(getattr(emcvnx, 'get_'+key)())
            args = [emcvnx.name, emcvnx.keys, vals]
            if self.auth_node:
                args += [(rcEnv.uuid, rcEnv.nodename)]
            self.proxy.update_emcvnx(*args)

    def push_netapp(self, objects=[], sync=True):
        if 'update_netapp' not in self.proxy_methods:
            print("'update_netapp' method is not exported by the collector")
            return
        m = __import__('rcNetapp')
        try:
            netapps = m.Netapps(objects)
        except:
            return
        for netapp in netapps:
            vals = []
            print(netapp.name)
            for key in netapp.keys:
                print(" extract", key)
                vals.append(getattr(netapp, 'get_'+key)())
            args = [netapp.name, netapp.keys, vals]
            if self.auth_node:
                args += [(rcEnv.uuid, rcEnv.nodename)]
            self.proxy.update_netapp(*args)

    def push_ibmsvc(self, objects=[], sync=True):
        if 'update_ibmsvc' not in self.proxy_methods:
            print("'update_ibmsvc' method is not exported by the collector")
            return
        m = __import__('rcIbmSvc')
        try:
            ibmsvcs = m.IbmSvcs(objects)
        except:
            return
        for ibmsvc in ibmsvcs:
            vals = []
            for key in ibmsvc.keys:
                vals.append(getattr(ibmsvc, 'get_'+key)())
            args = [ibmsvc.name, ibmsvc.keys, vals]
            if self.auth_node:
                args += [(rcEnv.uuid, rcEnv.nodename)]
            self.proxy.update_ibmsvc(*args)

    def push_nsr(self, sync=True):
        if 'update_nsr' not in self.proxy_methods:
           print("'update_nsr' method is not exported by the collector")
           return
        m = __import__('rcNsr')
        try:
            nsr = m.Nsr()
        except:
            return
        vals = []
        for key in nsr.keys:
            vals.append(getattr(nsr, 'get_'+key)())
        args = [rcEnv.nodename, nsr.keys, vals]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        try:
            self.proxy.update_nsr(*args)
        except:
            print("error pushing nsr index")

    def push_ibmds(self, objects=[], sync=True):
        if 'update_ibmds' not in self.proxy_methods:
           print("'update_ibmds' method is not exported by the collector")
           return
        m = __import__('rcIbmDs')
        try:
            ibmdss = m.IbmDss(objects)
        except:
            return
        for ibmds in ibmdss:
            vals = []
            for key in ibmds.keys:
                vals.append(getattr(ibmds, 'get_'+key)())
            args = [ibmds.name, ibmds.keys, vals]
            if self.auth_node:
                args += [(rcEnv.uuid, rcEnv.nodename)]
            try:
                self.proxy.update_ibmds(*args)
            except:
                print("error pushing", ibmds.name)

    def push_gcedisks(self, objects=[], sync=True):
        if 'update_gcedisks' not in self.proxy_methods:
           print("'update_gcedisks' method is not exported by the collector")
           return
        m = __import__('rcGceDisks')
        try:
            arrays = m.GceDiskss(objects)
        except:
            return
        for array in arrays:
            vals = []
            for key in array.keys:
                vals.append(getattr(array, 'get_'+key)())
            args = [array.name, array.keys, vals]
            if self.auth_node:
                args += [(rcEnv.uuid, rcEnv.nodename)]
            try:
                self.proxy.update_gcedisks(*args)
            except Exception as e:
                print("error pushing %s: %s" % (array.name, str(e)))

    def push_freenas(self, objects=[], sync=True):
        if 'update_freenas' not in self.proxy_methods:
           print("'update_freenas' method is not exported by the collector")
           return
        m = __import__('rcFreenas')
        try:
            arrays = m.Freenass(objects)
        except:
            return
        for array in arrays:
            vals = []
            for key in array.keys:
                vals.append(getattr(array, 'get_'+key)())
            args = [array.name, array.keys, vals]
            if self.auth_node:
                args += [(rcEnv.uuid, rcEnv.nodename)]
            try:
                self.proxy.update_freenas(*args)
            except:
                print("error pushing", array.name)

    def push_xtremio(self, objects=[], sync=True):
        if 'update_xtremio' not in self.proxy_methods:
           print("'update_xtremio' method is not exported by the collector")
           return
        m = __import__('rcXtremio')
        try:
            arrays = m.Arrays(objects)
        except:
            return
        for array in arrays:
            vals = []
            for key in array.keys:
                vals.append(getattr(array, 'get_'+key)())
            args = [array.name, array.keys, vals]
            if self.auth_node:
                args += [(rcEnv.uuid, rcEnv.nodename)]
            try:
                self.proxy.update_xtremio(*args)
            except Exception as exc:
                print("error pushing", array.name, file=sys.stderr)
                print(exc, file=sys.stderr)
                raise ex.excError

    def push_dcs(self, objects=[], sync=True):
        if 'update_dcs' not in self.proxy_methods:
           print("'update_dcs' method is not exported by the collector")
           return
        m = __import__('rcDcs')
        try:
            dcss = m.Dcss(objects)
        except:
            return
        for dcs in dcss:
            vals = []
            for key in dcs.keys:
                vals.append(getattr(dcs, 'get_'+key)())
            args = [dcs.name, dcs.keys, vals]
            if self.auth_node:
                args += [(rcEnv.uuid, rcEnv.nodename)]
            try:
                self.proxy.update_dcs(*args)
            except:
                print("error pushing", dcs.name)

    def push_eva(self, objects=[], sync=True):
        if 'update_eva_xml' not in self.proxy_methods:
            print("'update_eva_xml' method is not exported by the collector")
            return
        m = __import__('rcEva')
        try:
            evas = m.Evas(objects)
        except:
            return
        for eva in evas:
            vals = []
            for key in eva.keys:
                vals.append(getattr(eva, 'get_'+key)())
            args = [eva.name, eva.keys, vals]
            if self.auth_node:
                args += [(rcEnv.uuid, rcEnv.nodename)]
            self.proxy.update_eva_xml(*args)

    def push_sym(self, objects=[], sync=True):
        import zlib
        if 'update_sym_xml' not in self.proxy_methods:
            print("'update_sym_xml' method is not exported by the collector")
            return 1
        m = __import__('rcSymmetrix')
        try:
            syms = m.Arrays(objects)
        except Exception as e:
            print(e)
            return 1
        r = 0
        for sym in syms:
            # can be too big for a single rpc
            print(sym.sid)
            for key in sym.keys:
                print(" extract", key)
                vars = [key]
                try:
                    vals = [xmlrpclib.Binary(zlib.compress(getattr(sym, 'get_'+key)()))]
                except Exception as e:
                    print(e)
                    continue
                args = [sym.sid, vars, vals]
                if self.auth_node:
                    args += [(rcEnv.uuid, rcEnv.nodename)]
                try:
                    print(" send   ", key)
                    self.proxy.update_sym_xml(*args)
                except Exception as e:
                    print(sym.sid, key, ":", e)
                    r = 1
                    continue
            # signal all files are received
            args = [sym.sid, [], []]
            if self.auth_node:
                args += [(rcEnv.uuid, rcEnv.nodename)]
            self.proxy.update_sym_xml(*args)
        return r

    def push_all(self, svcs, sync=True):
        args = [[svc.svcname for svc in svcs]]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        for svc in svcs:
            self.push_config(svc, sync=sync)
            self.push_containerinfo(svc, sync=sync)

    def push_checks(self, vars, vals, sync=True):
        if "push_checks" not in self.proxy_methods:
            print("'push_checks' method is not exported by the collector")
            return
        args = [vars, vals]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        self.proxy.push_checks(*args)

    def register_node(self, sync=True):
        return self.proxy.register_node(rcEnv.nodename)

    def comp_get_data(self, modulesets=[], sync=True):
        args = [rcEnv.nodename, modulesets]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.comp_proxy.comp_get_data_v2(*args)

    def comp_get_svc_data(self, svcname, modulesets=[], sync=True):
        args = [rcEnv.nodename, svcname, modulesets]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.comp_proxy.comp_get_svc_data_v2(*args)

    def comp_get_data_moduleset(self, sync=True):
        args = [rcEnv.nodename]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.comp_proxy.comp_get_data_moduleset(*args)

    def comp_get_svc_data_moduleset(self, svc, sync=True):
        args = [svc]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.comp_proxy.comp_get_svc_data_moduleset(*args)

    def comp_attach_moduleset(self, moduleset, sync=True):
        args = [rcEnv.nodename, moduleset]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.comp_proxy.comp_attach_moduleset(*args)

    def comp_attach_svc_moduleset(self, svc, moduleset, sync=True):
        args = [svc, moduleset]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.comp_proxy.comp_attach_svc_moduleset(*args)

    def comp_detach_svc_moduleset(self, svcname, moduleset, sync=True):
        args = [svcname, moduleset]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.comp_proxy.comp_detach_svc_moduleset(*args)

    def comp_detach_moduleset(self, moduleset, sync=True):
        args = [rcEnv.nodename, moduleset]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.comp_proxy.comp_detach_moduleset(*args)

    def comp_get_svc_ruleset(self, svcname, sync=True):
        args = [svcname]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.comp_proxy.comp_get_svc_ruleset(*args)

    def comp_get_ruleset(self, sync=True):
        args = [rcEnv.nodename]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.comp_proxy.comp_get_ruleset(*args)

    def comp_get_ruleset_md5(self, rset_md5, sync=True):
        args = [rset_md5]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.comp_proxy.comp_get_ruleset_md5(*args)

    def comp_attach_ruleset(self, ruleset, sync=True):
        args = [rcEnv.nodename, ruleset]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.comp_proxy.comp_attach_ruleset(*args)

    def comp_detach_svc_ruleset(self, svcname, ruleset, sync=True):
        args = [svcname, ruleset]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.comp_proxy.comp_detach_svc_ruleset(*args)

    def comp_attach_svc_ruleset(self, svcname, ruleset, sync=True):
        args = [svcname, ruleset]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.comp_proxy.comp_attach_svc_ruleset(*args)

    def comp_detach_ruleset(self, ruleset, sync=True):
        args = [rcEnv.nodename, ruleset]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.comp_proxy.comp_detach_ruleset(*args)

    def comp_list_ruleset(self, pattern='%', sync=True):
        args = [pattern, rcEnv.nodename]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.comp_proxy.comp_list_rulesets(*args)

    def comp_list_moduleset(self, pattern='%', sync=True):
        args = [pattern]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.comp_proxy.comp_list_modulesets(*args)

    def comp_log_actions(self, vars, vals, sync=True):
        args = [vars, vals]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.comp_proxy.comp_log_actions(*args)

    def comp_show_status(self, svcname, pattern='%', sync=True):
        args = [svcname, pattern]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.comp_proxy.comp_show_status(*args)

    def collector_update_root_pw(self, opts, sync=True):
        args = [opts]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.proxy.collector_update_root_pw(*args)

    def collector_ack_unavailability(self, opts, sync=True):
        args = [opts]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.proxy.collector_ack_unavailability(*args)

    def collector_list_unavailability_ack(self, opts, sync=True):
        args = [opts]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.proxy.collector_list_unavailability_ack(*args)

    def collector_list_actions(self, opts, sync=True):
        args = [opts]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.proxy.collector_list_actions(*args)

    def collector_ack_action(self, opts, sync=True):
        args = [opts]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.proxy.collector_ack_action(*args)

    def collector_status(self, opts, sync=True):
        args = [opts]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.proxy.collector_status(*args)

    def collector_asset(self, opts, sync=True):
        args = [opts]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.proxy.collector_asset(*args)

    def collector_networks(self, opts, sync=True):
        args = [opts]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.proxy.collector_networks(*args)

    def collector_checks(self, opts, sync=True):
        args = [opts]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.proxy.collector_checks(*args)

    def collector_disks(self, opts, sync=True):
        args = [opts]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.proxy.collector_disks(*args)

    def collector_alerts(self, opts, sync=True):
        args = [opts]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.proxy.collector_alerts(*args)

    def collector_show_actions(self, opts, sync=True):
        args = [opts]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.proxy.collector_show_actions(*args)

    def collector_events(self, opts, sync=True):
        args = [opts]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.proxy.collector_events(*args)

    def collector_tag(self, opts, sync=True):
        args = [opts]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.proxy.collector_tag(*args)

    def collector_untag(self, opts, sync=True):
        args = [opts]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.proxy.collector_untag(*args)

    def collector_create_tag(self, opts, sync=True):
        args = [opts]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.proxy.collector_create_tag(*args)

    def collector_show_tags(self, opts, sync=True):
        args = [opts]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.proxy.collector_show_tags(*args)

    def collector_list_tags(self, opts, sync=True):
        args = [opts]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.proxy.collector_list_tags(*args)

    def collector_list_nodes(self, opts, sync=True):
        args = [opts]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.proxy.collector_list_nodes(*args)

    def collector_list_services(self, opts, sync=True):
        args = [opts]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.proxy.collector_list_services(*args)

    def collector_list_filtersets(self, opts, sync=True):
        args = [opts]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.proxy.collector_list_filtersets(*args)

    def collector_get_action_queue(self, sync=True):
        args = [rcEnv.nodename]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.proxy.collector_get_action_queue(*args)

    def collector_update_action_queue(self, data, sync=True):
        args = [data]
        if self.auth_node:
            args += [(rcEnv.uuid, rcEnv.nodename)]
        return self.proxy.collector_update_action_queue(*args)


if __name__ == "__main__":
    x = Collector()
    x.init()
    print(x.proxy_methods)
    print(x.comp_proxy_methods)
  0707010005ca19000041ed0000000000000000000000035ab2183a00000000000000660000000000000000000000000000000900000000root/var  0707010005ca1a000041ed0000000000000000000000035ab2183a00000000000000660000000000000000000000000000000d00000000root/var/lib  0707010005ca1b000041ed0000000000000000000000035ab2183a00000000000000660000000000000000000000000000001500000000root/var/lib/opensvc  0707010005ca1c000041ed0000000000000000000000035ab2183a00000000000000660000000000000000000000000000002000000000root/var/lib/opensvc/compliance   0707010005ca1d000041ed0000000000000000000000025ab2183a00000000000000660000000000000000000000000000002c00000000root/var/lib/opensvc/compliance/com.opensvc   0707010005ca1e000081fd0000000000000000000000015ab21754000014a5000000660000000000000000000000000000004000000000root/var/lib/opensvc/compliance/com.opensvc/ansible_playbook.py   #!/usr/bin/env python

data = {
  "default_prefix": "OSVC_COMP_ANSIBLE_PLAYBOOK_",
  "example_value": """ 
{
  "path": "/some/path/to/file",
  "fmt": "---"
}
  """,
  "description": """* Fetch a playbook from a href if required
* Run the playbook in check mode on check action
* Run the playbook on fix action
""",
  "form_definition": """
Desc: |
  Define or point to a ansible playbook.
Css: comp48

Outputs:
  -
    Dest: compliance variable
    Class: file
    Type: json
    Format: dict

Inputs:
  -
    Id: ref
    Label: Content URL pointer
    DisplayModeLabel: ref
    LabelCss: loc
    Help: "Examples:
        /path/to/reference_file
        http://server/path/to/reference_file
        https://server/path/to/reference_file
        ftp://server/path/to/reference_file
        ftp://login:pass@server/path/to/reference_file"
    Type: string
  -
    Id: fmt
    Label: Content
    DisplayModeLabel: fmt
    LabelCss: hd16
    Css: pre
    Help: A reference content for the file. The text can embed substitution variables specified with %%ENV:VAR%%.
    Type: text
"""
}

import os
import sys
import stat
import re
import tempfile
from subprocess import *

sys.path.append(os.path.dirname(__file__))

from comp import *

class InitError(Exception):
    pass

class AnsiblePlaybook(CompObject):
    def __init__(self, prefix=None):
        CompObject.__init__(self, prefix=prefix, data=data)

    def init(self):
        self.rules = []
        self.inventory = os.path.join(os.environ["OSVC_PATH_COMP"], ".ansible-inventory")

        for rule in self.get_rules():
            try:
                self.rules += self.add_rule(rule)
            except InitError:
                continue
            except ValueError:
                perror('ansible_playbook: failed to parse variable', os.environ[k])

        if len(self.rules) == 0:
            raise NotApplicable()

    def add_rule(self, d):
        if 'fmt' not in d and 'ref' not in d:
            perror('file: fmt or ref should be in the dict:', d)
            RET = RET_ERR
            return []
        if 'fmt' in d and 'ref' in d:
            perror('file: fmt and ref are exclusive:', d)
            RET = RET_ERR
            return []
        return [d]

    def download(self, d):
        if 'ref' in d and d['ref'].startswith("safe://"):
            return self.get_safe_file(d["ref"])
        elif 'fmt' in d and d['fmt'] != "":
            return self.write_fmt(d)
        else:
            return self.download_url()

    def download_url(self, d):
        f = tempfile.NamedTemporaryFile()
        tmpf = f.name
        f.close()
        try:
            self.urlretrieve(d['ref'], tmpf)
        except IOError as e:
            perror("file ref", d['ref'], "download failed:", e)
            raise InitError()
        return tmpf

    def get_safe_file(self, uuid):
        tmpf = tempfile.NamedTemporaryFile()
        tmpfname = tmpf.name
        tmpf.close()
        try:
            self.collector_safe_file_download(uuid, tmpfname)
        except Exception as e:
            raise ComplianceError("%s: %s" % (uuid, str(e)))
        return tmpfname

    def write_fmt(self, f):
        tmpf = tempfile.NamedTemporaryFile()
        tmpfname = tmpf.name
        tmpf.close()
        with open(tmpfname, 'w') as tmpf:
            tmpf.write(f['fmt'])
        return tmpfname

    def write_inventory(self):
        if os.path.exists(self.inventory):
            return
        with open(self.inventory, 'w') as ofile:
            ofile.write("[local]\n127.0.0.1\n")

    def fixable(self):
        return RET_NA

    def fix_playbook(self, rule, verbose=False):
        tmpfname = self.download(rule)
        try:
            return self._fix_playbook(rule, tmpfname, verbose=verbose)
        finally:
            os.unlink(tmpfname)

    def _fix_playbook(self, rule, tmpfname, verbose=False):
        self.write_inventory()
        cmd = ["ansible-playbook", "-c", "local", "-i", self.inventory, tmpfname]
        proc = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = proc.communicate()
        pinfo(out)
        perror(err)
        if proc.returncode != 0:
            return RET_ERR
        if "failed=0" in out:
            return RET_OK
        return RET_ERR

    def check_playbook(self, rule, verbose=False):
        tmpfname = self.download(rule)
        try:
            return self._check_playbook(rule, tmpfname, verbose=verbose)
        finally:
            os.unlink(tmpfname)

    def _check_playbook(self, rule, tmpfname, verbose=False):
        self.write_inventory()
        cmd = ["ansible-playbook", "-c", "local", "-i", self.inventory, "--check", tmpfname]
        proc = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = proc.communicate()
        pinfo(out)
        perror(err)
        if proc.returncode != 0:
            return RET_ERR
        if "changed=0" in out and "failed=0" in out:
            return RET_OK
        return RET_ERR

    def check(self):
        r = 0
        for rule in self.rules:
            r |= self.check_playbook(rule, verbose=True)
        return r

    def fix(self):
        r = 0
        for rule in self.rules:
            r |= self.fix_playbook(rule)
        return r

if __name__ == "__main__":
    main(AnsiblePlaybook)

   0707010005ca1f000081fd0000000000000000000000015ab2175400004359000000660000000000000000000000000000003700000000root/var/lib/opensvc/compliance/com.opensvc/authkey.py    #!/usr/bin/env python

data = {
  "default_prefix": "OSVC_COMP_AUTHKEY_",
  "example_value": """ 
    {
      "action": "add",
      "authfile": "authorized_keys",
      "user": "testuser",
      "key": "ssh-dss AAAAB3NzaC1kc3MAAACBAPiO1jlT+5yrdPLfQ7sYF52NkfCEzT0AUUNIl+14Sbkubqe+TcU7U3taUtiDJ5YOGOzIVFIDGGtwD0AqNHQbvsiS1ywtC5BJ9362FlrpVH4o1nVZPvMxRzz5hgh3HjxqIWqwZDx29qO8Rg1/g1Gm3QYCxqPFn2a5f2AUiYqc1wtxAAAAFQC49iboZGNqssicwUrX6TUrT9H0HQAAAIBo5dNRmTF+Vd/+PI0JUOIzPJiHNKK9rnySlaxSDml9hH2LuDSjYz7BWuNP8UnPOa2pcFA4meDp5u8d5dGOWxkuYO0bLnXwDZuHtDW/ySytjwEaBLPxoqRBAyfyQNlusGsuiqDYRA7j7bS0RxINBxvDw79KdyQhuOn8/lKVG+sjrQAAAIEAoShly/JlGLQxQzPyWADV5RFlaRSPaPvFzcYT3hS+glkVd6yrCbzc30Yc8Ndu4cflQiXSZzRoUMgsy5PzuiH1M8JjwHTGNl8r9OfJpnN/OaAhMpIyA06y1ZZD9iEME3UmthFQoZnfRuE3yxi7bqyXJU4rOq04iyCTpU1UKInPdXQ= testuser"
    }
  """,
  "description": """* Installs or removes ssh public keys from authorized_key files
* Looks up the authorized_key and authorized_key2 file location in the running sshd daemon configuration.
* Add user to sshd_config AllowUser and AllowGroup if used
* Reload sshd if sshd_config has been changed
""",
  "form_definition": """
Desc: |
  Describe a list of ssh public keys to authorize login as the specified Unix user.
Css: comp48

Outputs:
  -
    Dest: compliance variable
    Type: json
    Format: dict
    Class: authkey

Inputs:
  -
    Id: action
    Label: Action
    DisplayModeLabel: action
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Candidates:
      - add
      - del
    Help: Defines wether the public key must be installed or uninstalled.

  -
    Id: user
    Label: User
    DisplayModeLabel: user
    LabelCss: guy16
    Mandatory: Yes
    Type: string
    Help: Defines the Unix user name who will accept those ssh public keys.

  -
    Id: key
    Label: Public key
    DisplayModeLabel: key
    LabelCss: guy16
    Mandatory: Yes
    Type: text
    DisplayModeTrim: 60
    Help: The ssh public key as seen in authorized_keys files.

  -
    Id: authfile
    Label: Authorized keys file name
    DisplayModeLabel: authfile
    LabelCss: hd16
    Mandatory: Yes
    Candidates:
      - authorized_keys
      - authorized_keys2
    Default: authorized_keys2
    Type: string
    Help: The authorized_keys file to write the keys into.
"""
}

import os
import sys
import pwd, grp
import datetime
import shutil
from subprocess import *

sys.path.append(os.path.dirname(__file__))

from comp import *

class CompAuthKeys(CompObject):
    def __init__(self, prefix=None):
        CompObject.__init__(self, prefix=prefix, data=data)

    def init(self):
        self.authkeys = self.get_rules()

        for ak in self.authkeys:
            ak['key'] = ak['key'].replace('\n', '')

        self.installed_keys_d = {}
        self.default_authfile = "authorized_keys2"
        self.allowusers_check_done = []
        self.allowusers_fix_todo = []
        self.allowgroups_check_done = []
        self.allowgroups_fix_todo = []

    def sanitize(self, ak):
        if 'user' not in ak:
            perror("no user set in rule")
            return False
        if 'key' not in ak:
            perror("no key set in rule")
            return False
        if 'action' not in ak:
            ak['action'] = 'add'
        if 'authfile' not in ak:
            ak['authfile'] = self.default_authfile
        if ak['authfile'] not in ("authorized_keys", "authorized_keys2"):
            perror("unsupported authfile:", ak['authfile'], "(default to", self.default_authfile+")")
            ak['authfile'] = self.default_authfile
        for key in ('user', 'key', 'action', 'authfile'):
            ak[key] = ak[key].strip()
        return ak

    def fixable(self):
        return RET_NA

    def truncate_key(self, key):
        if len(key) < 50:
            s = key
        else:
            s = "'%s ... %s'" % (key[0:17], key[-30:])
        return s

    def reload_sshd(self):
        cmd = ['ps', '-ef']
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            perror("can not find sshd process")
            return RET_ERR
        out = bdecode(out)
        for line in out.splitlines():
            if not line.endswith('sbin/sshd'):
                continue
            l = line.split()
            pid = int(l[1])
            name = l[-1]
            pinfo("send sighup to pid %d (%s)" % (pid, name))
            os.kill(pid, 1)
            return RET_OK
        perror("can not find sshd process to signal")
        return RET_ERR

    def get_sshd_config(self):
        cfs = []
        if hasattr(self, "cache_sshd_config_f"):
            return self.cache_sshd_config_f

        cmd = ['ps', '-eo', 'comm']
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode == 0:
            out = bdecode(out)
            l = out.splitlines()
            if '/usr/local/sbin/sshd' in l:
                cfs.append(os.path.join(os.sep, 'usr', 'local', 'etc', 'sshd_config'))
            if '/usr/sfw/sbin/sshd' in l:
                cfs.append(os.path.join(os.sep, 'etc', 'sshd_config'))

        cfs += [os.path.join(os.sep, 'etc', 'ssh', 'sshd_config'),
                os.path.join(os.sep, 'opt', 'etc', 'sshd_config'),
                os.path.join(os.sep, 'etc', 'opt', 'ssh', 'sshd_config'),
                os.path.join(os.sep, 'usr', 'local', 'etc', 'sshd_config')]
        cf = None
        for _cf in cfs:
            if os.path.exists(_cf):
                cf = _cf
                break
        self.cache_sshd_config_f = cf
        if cf is None:
            perror("sshd_config not found")
            return None
        return cf

    def _get_authkey_file(self, key):
        if key == "authorized_keys":
            # default
            return ".ssh/authorized_keys"
        elif key == "authorized_keys2":
            key = "AuthorizedKeysFile"
        else:
            perror("unknown key", key)
            return None


        cf = self.get_sshd_config()
        if cf is None:
            perror("sshd_config not found")
            return None
        with open(cf, 'r') as f:
            buff = f.read()
        for line in buff.split('\n'):
            l = line.split()
            if len(l) != 2:
                continue
            if l[0].strip() == key:
                return l[1]
        # not found, return default
        return ".ssh/authorized_keys2"

    def get_allowusers(self):
        if hasattr(self, "cache_allowusers"):
            return self.cache_allowusers
        cf = self.get_sshd_config()
        if cf is None:
            perror("sshd_config not found")
            return None
        with open(cf, 'r') as f:
            buff = f.read()
        for line in buff.split('\n'):
            l = line.split()
            if len(l) < 2:
                continue
            if l[0].strip() == "AllowUsers":
                self.cache_allowusers = l[1:]
                return l[1:]
        self.cache_allowusers = None
        return None

    def get_allowgroups(self):
        if hasattr(self, "cache_allowgroups"):
            return self.cache_allowgroups
        cf = self.get_sshd_config()
        if cf is None:
            perror("sshd_config not found")
            return None
        with open(cf, 'r') as f:
            buff = f.read()
        for line in buff.split('\n'):
            l = line.split()
            if len(l) < 2:
                continue
            if l[0].strip() == "AllowGroups":
                self.cache_allowgroups = l[1:]
                return l[1:]
        self.cache_allowgroups = None
        return None

    def get_authkey_file(self, key, user):
        p = self._get_authkey_file(key)
        if p is None:
            return None
        p = p.replace('%u', user)
        p = p.replace('%h', os.path.expanduser('~'+user))
        p = p.replace('~', os.path.expanduser('~'+user))
        if not p.startswith('/'):
            p = os.path.join(os.path.expanduser('~'+user), p)
        return p

    def get_authkey_files(self, user):
        l = []
        p = self.get_authkey_file('authorized_keys', user)
        if p is not None:
            l.append(p)
        p = self.get_authkey_file('authorized_keys2', user)
        if p is not None:
            l.append(p)
        return l

    def get_installed_keys(self, user):
        if user in self.installed_keys_d:
            return self.installed_keys_d[user]
        else:
            self.installed_keys_d[user] = []

        ps = self.get_authkey_files(user)
        for p in ps:
            if not os.path.exists(p):
                continue
            with open(p, 'r') as f:
                self.installed_keys_d[user] += f.read().splitlines()
        return self.installed_keys_d[user]

    def get_user_group(self, user):
        gid = pwd.getpwnam(user).pw_gid
        try:
            gname = grp.getgrgid(gid).gr_name
        except KeyError:
            gname = None
        return gname

    def fix_allowusers(self, ak, verbose=True):
        self.check_allowuser(ak, verbose=False)
        if not ak['user'] in self.allowusers_fix_todo:
            return RET_OK
        self.allowusers_fix_todo.remove(ak['user'])
        au = self.get_allowusers()
        if au is None:
            return RET_OK
        l = ["AllowUsers"] + au + [ak['user']]
        s = " ".join(l)

        pinfo("adding", ak['user'], "to currently allowed users")
        cf = self.get_sshd_config()
        if cf is None:
            perror("sshd_config not found")
            return None
        with open(cf, 'r') as f:
            buff = f.read()
        lines = buff.split('\n')
        for i, line in enumerate(lines):
            l = line.split()
            if len(l) < 2:
                continue
            if l[0].strip() == "AllowUsers":
                lines[i] = s
        buff = "\n".join(lines)
        self.backup(cf)
        with open(cf, 'w') as f:
            f.write(buff)
        self.reload_sshd()
        return RET_OK

    def fix_allowgroups(self, ak, verbose=True):
        self.check_allowgroup(ak, verbose=False)
        if not ak['user'] in self.allowgroups_fix_todo:
            return RET_OK
        self.allowgroups_fix_todo.remove(ak['user'])
        ag = self.get_allowgroups()
        if ag is None:
            return RET_OK
        ak['group'] = self.get_user_group(ak['user'])
        if ak['group'] is None:
            perror("can not set AllowGroups in sshd_config: primary group of user %s not found" % ak['user'])
            return RET_ERR
        l = ["AllowGroups"] + ag + [ak['group']]
        s = " ".join(l)

        pinfo("adding", ak['group'], "to currently allowed groups")
        cf = self.get_sshd_config()
        if cf is None:
            perror("sshd_config not found")
            return RET_ERR
        with open(cf, 'r') as f:
            buff = f.read()
        lines = buff.split('\n')
        for i, line in enumerate(lines):
            l = line.split()
            if len(l) < 2:
                continue
            if l[0].strip() == "AllowGroups":
                lines[i] = s
        buff = "\n".join(lines)
        self.backup(cf)
        with open(cf, 'w') as f:
            f.write(buff)
        self.reload_sshd()
        return RET_OK

    def check_allowuser(self, ak, verbose=True):
        if ak['user'] in self.allowusers_check_done:
            return RET_OK
        self.allowusers_check_done.append(ak['user'])
        au = self.get_allowusers()
        if au is None:
            return RET_OK
        elif ak['user'] in au:
            if verbose:
                pinfo(ak['user'], "is correctly set in sshd AllowUsers")
            r = RET_OK
        else:
            if verbose:
                perror(ak['user'], "is not set in sshd AllowUsers")
            self.allowusers_fix_todo.append(ak['user'])
            r = RET_ERR
        return r

    def check_allowgroup(self, ak, verbose=True):
        if ak['user'] in self.allowgroups_check_done:
            return RET_OK
        self.allowgroups_check_done.append(ak['user'])
        ag = self.get_allowgroups()
        if ag is None:
            return RET_OK
        ak['group'] = self.get_user_group(ak['user'])
        if ak['group'] is None:
            if verbose:
                perror("can not determine primary group of user %s to add to AllowGroups" % ak['user'])
            return RET_ERR
        elif ak['group'] in ag:
            if verbose:
                pinfo(ak['group'], "is correctly set in sshd AllowGroups")
            r = RET_OK
        else:
            if verbose:
                perror(ak['group'], "is not set in sshd AllowGroups")
            self.allowgroups_fix_todo.append(ak['user'])
            r = RET_ERR
        return r

    def check_authkey(self, ak, verbose=True):
        ak = self.sanitize(ak)
        installed_keys = self.get_installed_keys(ak['user'])
        if ak['action'] == 'add':
            if ak['key'] not in installed_keys:
                if verbose:
                    perror('key', self.truncate_key(ak['key']), 'must be installed for user', ak['user'])
                r = RET_ERR
            else:
                if verbose:
                    pinfo('key', self.truncate_key(ak['key']), 'is correctly installed for user', ak['user'])
                r = RET_OK
        elif ak['action'] == 'del':
            if ak['key'] in installed_keys:
                if verbose:
                    perror('key', self.truncate_key(ak['key']), 'must be uninstalled for user', ak['user'])
                r = RET_ERR
            else:
                if verbose:
                    pinfo('key', self.truncate_key(ak['key']), 'is correctly not installed for user', ak['user'])
                r = RET_OK
        else:
            perror("unsupported action:", ak['action'])
            return RET_ERR
        return r

    def fix_authkey(self, ak):
        ak = self.sanitize(ak)
        if ak['action'] == 'add':
            r = self.add_authkey(ak)
            return r
        elif ak['action'] == 'del':
            return self.del_authkey(ak)
        else:
            perror("unsupported action:", ak['action'])
            return RET_ERR

    def add_authkey(self, ak):
        if self.check_authkey(ak, verbose=False) == RET_OK:
            return RET_OK

        try:
            userinfo=pwd.getpwnam(ak['user'])
        except KeyError:
            perror('user', ak['user'], 'does not exist')
            return RET_ERR

        p = self.get_authkey_file(ak['authfile'], ak['user'])
        if p is None:
            perror("could not determine", ak['authfile'], "location")
            return RET_ERR
        base = os.path.dirname(p)

        if not os.path.exists(base):
            os.makedirs(base, 0o0700)
            pinfo(base, "created")
            if p.startswith(os.path.expanduser('~'+ak['user'])):
                os.chown(base, userinfo.pw_uid, userinfo.pw_gid)
                pinfo(base, "ownership set to %d:%d"%(userinfo.pw_uid, userinfo.pw_gid))

        if not os.path.exists(p):
            with open(p, 'w') as f:
                f.write("")
                pinfo(p, "created")
                os.chmod(p, 0o0600)
                pinfo(p, "mode set to 0600")
                os.chown(p, userinfo.pw_uid, userinfo.pw_gid)
                pinfo(p, "ownership set to %d:%d"%(userinfo.pw_uid, userinfo.pw_gid))
        else:
            self.backup(p)

        with open(p, 'a') as f:
            f.write(ak['key'])
            if not ak['key'].endswith('\n'):
                f.write('\n')
            pinfo('key', self.truncate_key(ak['key']), 'installed for user', ak['user'])

        return RET_OK

    def del_authkey(self, ak):
        if self.check_authkey(ak, verbose=False) == RET_OK:
            pinfo('key', self.truncate_key(ak['key']), 'is already not installed for user', ak['user'])
            return RET_OK

        ps = self.get_authkey_files(ak['user'])

        for p in ps:
            base = os.path.basename(p)
            if not os.path.exists(p):
                continue

            with open(p, 'r') as f:
                l = f.read().split('\n')

            n = len(l)
            while True:
                try:
                    l.remove(ak['key'].replace('\n', ''))
                except ValueError:
                    break
            if len(l) == n:
                # nothing changed
                continue

            with open(p, 'w') as f:
                f.write('\n'.join(l))
                pinfo('key', self.truncate_key(ak['key']), 'uninstalled for user', ak['user'])

        return RET_OK

    def check(self):
        r = 0
        for ak in self.authkeys:
            r |= self.check_authkey(ak)
            if ak['action'] == 'add':
                r |= self.check_allowgroup(ak)
                r |= self.check_allowuser(ak)
        return r

    def fix(self):
        r = 0
        for ak in self.authkeys:
            r |= self.fix_authkey(ak)
            if ak['action'] == 'add':
                r |= self.fix_allowgroups(ak)
                r |= self.fix_allowusers(ak)
        return r

if __name__ == "__main__":
    main(CompAuthKeys)

   0707010005ca20000081fd0000000000000000000000015ab2175400000875000000660000000000000000000000000000003400000000root/var/lib/opensvc/compliance/com.opensvc/bios.py   #!/usr/bin/env python

data = {
  "default_prefix": "OSVC_COMP_BIOS_",
  "example_value": "0.6.0",
  "description": """* Checks an exact BIOS version, as returned by dmidecode or sysfs
* Module need to be called with the exposed bios version as variable (bios.py $OSVC_COMP_TEST_BIOS_1 check)
""",
}

import os
import sys
from subprocess import *

sys.path.append(os.path.dirname(__file__))

from comp import *

class CompBios(CompObject):
    def __init__(self, prefix=None):
        CompObject.__init__(self, prefix=prefix, data=data)

    def init(self):
        self.rules = self.get_rules_raw()
        self.sysname, self.nodename, x, x, self.machine = os.uname()
        if self.sysname not in ['Linux']:
            perror('module not supported on', self.sysname)
            raise NotApplicable()

    def get_bios_version_Linux(self):
        p = os.path.join(os.sep, 'sys', 'class', 'dmi', 'id', 'bios_version')
        try:
            f = open(p, 'r')
            ver = f.read().strip()
            f.close()
            return ver
        except:
            pass

        try:
            cmd = ['dmidecode', '-t', 'bios']
            p = Popen(cmd, stdout=PIPE)
            out, err = p.communicate()
            if p.returncode != 0:
                raise
            out = bdecode(out)
            for line in out.splitlines():
                if 'Version:' in line:
                    return line.split(':')[-1].strip()
            raise
        except:
            perror('can not fetch bios version')
            return None
        return ver

    def fixable(self):
        return RET_NA

    def check(self):
        self.ver = self.get_bios_version_Linux()
        if self.ver is None:
            return RET_NA
        r = RET_OK
        for rule in self.rules:
            r |= self._check(rule)
        return r

    def _check(self, rule):
        if self.ver == rule:
            pinfo("bios version is %s, on target" % self.ver)
            return RET_OK
        perror("bios version is %s, target %s" % (self.ver, rule))
        return RET_ERR

    def fix(self):
        return RET_NA

if __name__ == "__main__":
    main(CompBios)
   0707010005ca21000081fd0000000000000000000000015ab2175400000e53000000660000000000000000000000000000003900000000root/var/lib/opensvc/compliance/com.opensvc/chkconfig.py  #!/usr/bin/env python

from subprocess import *
import sys
import os

sys.path.append(os.path.dirname(__file__))

from comp import *

os.environ['LANG'] = 'C'

class InitError(Exception):
    pass

class UnknownService(Exception):
    pass

class SetError(Exception):
    pass

class Chkconfig(object):
    def __init__(self):
        self.load()

    def __str__(self):
        s = ""
        for svc in self.services:
            s += "%-20s %s\n"%(svc, ' '.join(map(lambda x: '%-4s'%x,  self.services[svc])))
        return s

    def load(self):
        self.services = {}

        p = Popen(['/sbin/chkconfig', '--list'], stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            raise InitError()
        out = bdecode(out)
        for line in out.splitlines():
            words = line.split()
            if len(words) != 8:
                continue
            self.services[words[0]] = []
            for w in words[1:]:
                level, state = w.split(':')
                self.services[words[0]].append(state)

    def load_one(self, service):
        p = Popen(['/sbin/chkconfig', '--list', service], stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            out = bdecode(out)
            if 'not referenced' in out:
                self.services[service] = ['off', 'off', 'off', 'off', 'off', 'off']
                return
            raise InitError()

    def activate(self, service):
        p = Popen(['chkconfig', service, 'on'], stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            raise SetError()

    def set_state(self, service, level, state):
        curstate = self.get_state(service, level)
        if curstate == state:
            return
        p = Popen(['chkconfig', '--level', level, service, state], stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            raise SetError()

    def get_state(self, service, level):
        if service not in self.services:
            try:
                self.load_one(service)
            except InitError:
                pass

        if service not in self.services:
            raise UnknownService()

        return self.services[service][level]

    def check_state(self, service, levels, state, seq=None, verbose=False):
        r = 0
        for level in levels:
            try:
                level = int(level)
            except:
                continue
            try:
                curstate = self.get_state(service, level)
            except UnknownService:
                if verbose:
                    perror("can not get service", service, "runlevels")
                return 1
            if curstate != state:
                if verbose:
                    perror("service", service, "at runlevel", level, "is in state", curstate, "! target state is", state)
                r |= 1
            else:
                if verbose:
                    pinfo("service", service, "at runlevel", level, "is in state", curstate)
        return r
            
    def fix_state(self, service, levels, state, seq=None):
        cmd = ['chkconfig', '--level', levels, service, state]
        pinfo("exec:", ' '.join(cmd))
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            perror("failed to set", service, "runlevels")
            pinfo(out)
            perror(err)
            return 1
        return 0

if __name__ == "__main__":
    o = Chkconfig()
    pinfo(o)
    pinfo('xfs@rc3 =', o.get_state('xfs', 3))
 0707010005ca22000081b40000000000000000000000015ab2175400003de4000000660000000000000000000000000000003400000000root/var/lib/opensvc/compliance/com.opensvc/comp.py   #!/usr/bin/env python

from __future__ import print_function
import sys
import os
import re
import json
import base64

if sys.version_info[0] >= 3:
    from urllib.request import Request, urlopen
    from urllib.error import HTTPError
    from urllib.parse import urlencode
else:
    from urllib2 import Request, urlopen
    from urllib2 import HTTPError
    from urllib import urlencode

RET_OK = 0
RET_ERR = 1
RET_NA = 2

RET = RET_OK

class NotApplicable(Exception):
     pass

class Unfixable(Exception):
     pass

class ComplianceError(Exception):
     pass

class InitError(Exception):
     pass

class EndRecursion(Exception):
     pass

def pinfo(*args, **kwargs):
    if is_string(args) and len(args):
        return
    if isinstance(args, list) and (len(args) == 0 or len(args[0]) == 0):
        return
    kwargs["file"] = sys.stdout
    print(*args, **kwargs)

def perror(*args, **kwargs):
    if is_string(args) and len(args):
        return
    if isinstance(args, list) and (len(args) == 0 or len(args[0]) == 0):
        return
    kwargs["file"] = sys.stderr
    print(*args, **kwargs)

def is_string(s):
    """ python[23] compatible
    """
    if sys.version_info[0] == 2:
        l = (str, unicode)
    else:
        l = (str)
    if isinstance(s, l):
        return True
    return False

def bdecode(buff):
    if sys.version_info[0] < 3:
        return buff
    else:
        try:
            return str(buff, "utf-8")
        except:
            return str(buff, "ascii")
    return buff

def bencode(buff):
    if sys.version_info[0] < 3:
        return buff
    else:
        try:
            return bytes(buff, "utf-8")
        except:
            return bytes(buff, "ascii")
    return buff

class CompObject(object):
    def __init__(self,
                 prefix=None,
                 data={}):
        if prefix:
            self.prefix = prefix.upper()
        elif "default_prefix" in data:
            self.prefix = data["default_prefix"].upper()
        else:
            self.prefix = "MAGIX12345"

        self.extra_syntax_parms = data.get("extra_syntax_parms")
        self.example_value = data.get("example_value", "")
        self.example_kwargs = data.get("example_kwargs", {})
        self.example_env = data.get("example_env", {})
        self.description = data.get("description", "(no description)")
        self.form_definition = data.get("form_definition", "(no form definition)")
        self.init_done = False

    def __getattribute__(self, s):
        if not object.__getattribute__(self, "init_done") and s in ("check", "fix", "fixable"):
            object.__setattr__(self, "init_done", True)
            object.__getattribute__(self, "init")()
        return object.__getattribute__(self, s)

    def init(self):
        pass

    def test(self):
        self.__init__(**self.example_kwargs)
        self.prefix = "OSVC_COMP_CO_TEST"
        for k, v in self.example_env.items():
            self.set_env(k, v)
        self.set_env(self.prefix, self.example_value)
        return self.check()

    def info(self):
        def indent(text):
            lines = text.split("\n")
            return "\n".join(["    "+line for line in lines])
        s = ""
        s += "Description\n"
        s += "===========\n"
        s += "\n"
        s += indent(self.description)+"\n"
        s += "\n"
        s += "Example rule\n"
        s += "============\n"
        s += "\n::\n\n"
        s += indent(json.dumps(json.loads(self.example_value), indent=4, separators=(',', ': ')))+"\n"
        s += "\n"
        s += "Form definition\n"
        s += "===============\n"
        s += "\n::\n\n"
        s += indent(self.form_definition)+"\n"
        s += "\n"
        pinfo(s)

    def set_prefix(self, prefix):
        self.prefix = prefix.upper()

    def set_env(self, k, v):
        if sys.version_info[0] < 3:
            v = v.decode("utf-8")
        os.environ[k] = v

    def get_env(self, k):
        s = os.environ[k]
        if sys.version_info[0] < 3:
            s = s.encode("utf-8")
        return s

    def get_rules_raw(self):
        rules = []
        for k in [key for key in os.environ if key.startswith(self.prefix)]:
            s = self.subst(self.get_env(k))
            rules += [s]
        if len(rules) == 0:
            raise NotApplicable("no rules (%s)" % self.prefix)
        return rules

    def encode_data(self, data):
        if sys.version_info[0] > 2:
            return data
        if type(data) == dict:
            for k in data:
                if isinstance(data[k], (str, unicode)):
                    data[k] = data[k].encode("utf-8")
                elif isinstance(data[k], (list, dict)):
                    data[k] = self.encode_data(data[k])
        elif type(data) == list:
            for i, v in enumerate(data):
                if isinstance(v, (str, unicode)):
                    data[i] = v.encode("utf-8")
                elif isinstance(data[i], (list, dict)):
                    data[i] = self.encode_data(data[i])
        return data

    def get_rules(self):
        return [self.encode_data(v[1]) for v in self.get_rule_items()]

    def get_rule_items(self):
        rules = []
        for k in [key for key in os.environ if key.startswith(self.prefix)]:
            try:
                s = self.subst(self.get_env(k))
            except Exception as e:
                perror(k, e)
                continue
            try:
                data = json.loads(s)
            except ValueError:
                perror("failed to concatenate '%s=%s' to rules list" % (k, str(self.get_env(k))))
                continue
            if type(data) == list:
                for d in data:
                    rules += [(k, d)]
            else:
                rules += [(k, data)]
        if len(rules) == 0:
            raise NotApplicable("no rules (%s)" % self.prefix)
        return rules

    def subst(self, v):
        """
          A rule value can contain references to other rules as %%ENV:OTHER%%.
          This function substitutes these markers with the referenced rules values,
          which may themselves contain references. Hence the recursion.
        """
        max_recursion = 10

        if type(v) == list:
            l = []
            for _v in v:
                l.append(self.subst(_v))
            return l
        if type(v) != str and type(v) != unicode:
            return v

        p = re.compile('%%ENV:\w+%%', re.IGNORECASE)

        def _subst(v):
            matches = p.findall(v)
            if len(matches) == 0:
                raise EndRecursion
            for m in matches:
                s = m.strip("%").upper().replace('ENV:', '')
                if s in os.environ:
                    _v = self.get_env(s)
                elif 'OSVC_COMP_'+s in os.environ:
                    _v = self.get_env('OSVC_COMP_'+s)
                else:
                    _v = ""
                    raise NotApplicable("undefined substitution variable: %s" % s)
                v = v.replace(m, _v)
            return v

        for i in range(max_recursion):
            try:
                v = _subst(v)
            except EndRecursion:
                break

        return v

    def collector_api(self):
        if hasattr(self, "collector_api_cache"):
            return self.collector_api_cache
        import platform
        sysname, nodename, x, x, machine, x = platform.uname()
        try:
            import ConfigParser
        except ImportError:
            import configparser as ConfigParser
        config = ConfigParser.RawConfigParser({})
        if os.path.realpath(__file__).startswith("/opt/opensvc"):
            config.read("/opt/opensvc/etc/node.conf")
        else:
            config.read("/etc/opensvc/node.conf")
        data = {}
        svcname = os.environ.get("OSVC_COMP_SERVICES_SVCNAME")
        if svcname:
            data["username"] = svcname+"@"+nodename
        else:
            data["username"] = nodename
        data["password"] = config.get("node", "uuid")
        data["url"] = config.get("node", "dbopensvc").replace("/feed/default/call/xmlrpc", "")
        data["url"] = data["url"].replace("/init/rest/api", "")
        data["url"] += "/init/rest/api"
        self.collector_api_cache = data
        return self.collector_api_cache

    def collector_url(self):
        api = self.collector_api()
        s = "%s:%s@" % (api["username"], api["password"])
        url = api["url"].replace("https://", "")
        url = url.replace("http://", "")
        url = "https://"+s+url
        return url

    def collector_request(self, path):
        api = self.collector_api()
        url = api["url"]
        request = Request(url+path)
        base64string = base64.encodestring('%s:%s' % (api["username"], api["password"])).replace('\n', '')
        request.add_header("Authorization", "Basic %s" % base64string)
        return request

    def collector_rest_get(self, path):
        api = self.collector_api()
        request = self.collector_request(path)
        if api["url"].startswith("https"):
            try:
                import ssl
                kwargs = {"context": ssl._create_unverified_context()}
            except:
                kwargs = {}
        else:
            raise ComplianceError("refuse to submit auth tokens through a non-encrypted transport")
        try:
            f = urlopen(request, **kwargs)
        except HTTPError as e:
            try:
                err = json.loads(e.read())["error"]
                e = ComplianceError(err)
            except:
                pass
            raise e
        import json
        data = json.loads(f.read())
        f.close()
        return data

    def collector_rest_get_to_file(self, path, fpath):
        api = self.collector_api()
        request = self.collector_request(path)
        if api["url"].startswith("https"):
            try:
                import ssl
                kwargs = {"context": ssl._create_unverified_context()}
            except:
                kwargs = {}
        else:
            raise ComplianceError("refuse to submit auth tokens through a non-encrypted transport")
        try:
            f = urlopen(request, **kwargs)
        except HTTPError as e:
            try:
                err = json.loads(e.read())["error"]
                e = ComplianceError(err)
            except:
                pass
            raise e
        with open(fpath, 'wb') as df:
            for chunk in iter(lambda: f.read(4096), b""):
                df.write(chunk)
        f.close()

    def collector_safe_uri_to_uuid(self, uuid):
        if uuid.startswith("safe://"):
            uuid = uuid.replace("safe://", "")
        if not uuid.startswith("safe"):
            raise ComplianceError("malformed safe file uri: %s" % uuid)
        return uuid

    def collector_safe_file_download(self, uuid, fpath):
        uuid = self.collector_safe_uri_to_uuid(uuid)
        self.collector_rest_get_to_file("/safe/" + uuid + "/download", fpath)

    def collector_safe_file_get_meta(self, uuid):
        uuid = self.collector_safe_uri_to_uuid(uuid)
        data = self.collector_rest_get("/safe/" + uuid)
        if len(data["data"]) == 0:
            raise ComplianceError(uuid + ": metadata not found")
        return data["data"][0]

    def urlretrieve(self, url, fpath):
        request = Request(url)
        kwargs = {}
        if sys.hexversion >= 0x02070900:
            import ssl
            kwargs["context"] = ssl._create_unverified_context()
        f = urlopen(request, **kwargs)
        with open(fpath, 'wb') as df:
            for chunk in iter(lambda: f.read(4096), b""):
                df.write(chunk)

    def md5(self, fpath):
        import hashlib
        hash = hashlib.md5()
        with open(fpath, 'rb') as f:
            for chunk in iter(lambda: f.read(4096), b""):
                hash.update(chunk)
        return hash.hexdigest()

    def backup(self, path):
        import shutil
        if not os.path.exists(path):
            return
        session_uuid = os.environ.get("OSVC_SESSION_UUID")
        pathvar = os.environ.get("OSVC_PATH_VAR")
        if session_uuid is None or pathvar is None:
            return
        backup_base_d = os.path.join(pathvar, "compliance_backup", session_uuid)
        backup_f = os.path.join(backup_base_d, path.lstrip(os.sep))
        if os.path.exists(backup_f):
            return
        backup_d = os.path.dirname(backup_f)
        if not os.path.exists(backup_d):
            try:
                os.makedirs(backup_d)
            except OSError as exc:
                perror("failed to backup %s: create dir %s" % (path, backup_d))
                raise ComplianceError()
        try:
            shutil.copy(path, backup_f)
        except Exception:
            perror("failed to backup %s: copy to %s" % (path, backup_f))
            raise ComplianceError()
        self.remove_old_backups()

    def restore(self, path):
        import shutil
        if not os.path.exists(path):
            return
        session_uuid = os.environ.get("OSVC_SESSION_UUID")
        if session_uuid is None:
            return
        backup_base_d = os.path.join(pathvar, "compliance_backup", session_uuid)
        backup_f = os.path.join(backup_base_d, path)
        if not backup_f:
            perror("failed to restore %s: no backup" % path)
            raise ComplianceError()
        try:
            shutil.copy(backup, path)
            pinfo("%s restored" % path)
        except Exception:
            perror("failed to restore %s" % path)
            raise ComplianceError()

    def remove_old_backups(self):
        import glob
        import time
        import shutil
        threshold = time.time() - 7 * 24 * 60 * 60
        pathvar = os.environ.get("OSVC_PATH_VAR")
        for path in glob.glob(os.path.join(pathvar, "compliance_backup", "*-*-*-*-*")):
            mtime = os.stat(path).st_mtime
            if mtime < threshold:
                shutil.rmtree(path)
            

    #
    # Placeholders, to override in child class
    #
    def check(self):
        return RET_NA

    def fixable(self):
        return RET_NA

    def fix(self):
        return RET_NA



def main(co):
    syntax =  "syntax:\n"
    syntax += """ %s <ENV VARS PREFIX> check|fix|fixable\n"""%sys.argv[0]
    syntax += """ %s test|info"""%sys.argv[0]

    try:
        o = co()
    except NotApplicable as e:
        pinfo(e)
        sys.exit(RET_NA)
    if o.extra_syntax_parms:
        syntax += " "+o.extra_syntax_parms

    if len(sys.argv) == 2:
        if sys.argv[1] == 'test':
            try:
                RET = o.test()
                sys.exit(RET)
            except ComplianceError as e:
                perror(e)
                sys.exit(RET_ERR)
            except NotApplicable:
                sys.exit(RET_NA)
        elif sys.argv[1] == 'info':
            o.info()
            sys.exit(0)

    if len(sys.argv) < 3:
        perror(syntax)
        sys.exit(RET_ERR)

    argv = [sys.argv[1]]
    if len(sys.argv) > 3:
        argv += sys.argv[3:] 
    o.__init__(*argv)
    try:
        if sys.argv[2] == 'check':
            RET = o.check()
        elif sys.argv[2] == 'fix':
            RET = o.fix()
        elif sys.argv[2] == 'fixable':
            RET = o.fixable()
        else:
            perror("unsupported argument '%s'"%sys.argv[2])
            perror(syntax)
            RET = RET_ERR
    except ComplianceError as e:
        perror(e)
        sys.exit(RET_ERR)
    except NotApplicable as e:
        pinfo(e)
        sys.exit(RET_NA)
    except:
        import traceback
        traceback.print_exc()
        sys.exit(RET_ERR)

    sys.exit(RET)

if __name__ == "__main__":
    perror("this file is for import into compliance objects")

0707010005ca23000081fd0000000000000000000000015ab2175400001ace000000660000000000000000000000000000003400000000root/var/lib/opensvc/compliance/com.opensvc/cron.py   #!/usr/bin/env python

data = {
  "default_prefix": "OSVC_COMP_CRON_ENTRY_",
  "example_value": "add:osvc:* * * * *:/path/to/mycron:/etc/cron.d/opensvc",
  "description": """* Add and Remove cron entries
* Support arbitrary cron file location
""",
}

import os
import sys
import shutil
import glob
from subprocess import *

sys.path.append(os.path.dirname(__file__))

from comp import *

class CompCron(CompObject):
    def __init__(self, prefix=None):
        CompObject.__init__(self, prefix=prefix, data=data)

    def init(self):
        self.sysname, self.nodename, x, x, self.machine = os.uname()

        if self.sysname == 'SunOS' :
            self.crontab_locs = [
                '/var/spool/cron/crontabs'
            ]
        else:
            self.crontab_locs = [
                '/etc/cron.d',
                '/var/spool/cron/crontabs',
                '/var/spool/cron',
                '/var/cron/tabs',
            ]

        self.ce = []
        for _ce in self.get_rules_raw():
                e = _ce.split(':')
                if len(e) < 5:
                    perror("malformed variable %s. format: action:user:sched:cmd:[file]"%_ce)
                    continue
                if e[0] not in ('add', 'del'):
                    perror("unsupported action in variable %s. set 'add' or 'del'"%_ce)
                    continue
                if len(e[2].split()) != 5:
                    perror("malformed schedule in variable %s"%_ce)
                    continue
                self.ce += [{
                        'var': _ce,
                        'action': e[0],
                        'user': e[1],
                        'sched': e[2],
                        'cmd': e[3],
                        'file': e[4],
                       }]

        if len(self.ce) == 0:
            raise NotApplicable()


    def activate_cron(self, cron_file):
        """ Activate changes (actually only needed on HP-UX)
        """
        if '/var/spool/' in cron_file:
            pinfo("tell crond about the change")
            cmd = ['crontab', cron_file]
            process = Popen(cmd, stdout=PIPE, stderr=PIPE, close_fds=True)
            buff = process.communicate()

    def fixable(self):
        r = RET_OK
        for e in self.ce:
            try:
                self._fixable_cron(e)
            except ComplianceError, e:
                perror(str(e))
                r = RET_ERR
            except Unfixable, e:
                perror(str(e))
                return r
        return r

    def fix(self):
        r = RET_OK
        for e in self.ce:
            try:
                if e['action'] == 'add':
                    self._add_cron(e)
                elif e['action'] == 'del':
                    self._del_cron(e)
            except ComplianceError, e:
                perror(str(e))
                r = RET_ERR
            except Unfixable, e:
                perror(str(e))
                return r
        return r

    def check(self):
        r = RET_OK
        for e in self.ce:
            try:
                self._check_cron(e)
            except ComplianceError, e:
                perror(str(e))
                r = RET_ERR
            except Unfixable, e:
                perror(str(e))
                return r
        return r

    def get_cron_file(self, e):
        """ order of preference
        """
        cron_file = None
        for loc in self.crontab_locs:
            if not os.path.exists(loc):
                continue
            if loc == '/etc/cron.d':
                 cron_file = os.path.join(loc, e['file'])
            else:
                 cron_file = os.path.join(loc, e['user'])
            break
        return cron_file

    def format_entry(self, cron_file, e):
        if 'cron.d' in cron_file:
            s = ' '.join([e['sched'], e['user'], e['cmd']])
        else:
            s = ' '.join([e['sched'], e['cmd']])
        return s

    def _fixable_cron(self, e):
        cron_file = self.get_cron_file(e)

        if cron_file is None:
            raise Unfixable("no crontab usual location found (%s)"%str(self.crontab_locs))

    def _check_cron(self, e):
        cron_file = self.get_cron_file(e)

        if cron_file is None:
            raise Unfixable("no crontab usual location found (%s)"%str(self.crontab_locs))

        s = self.format_entry(cron_file, e)

        if not os.path.exists(cron_file):
            raise ComplianceError("cron entry not found '%s' in '%s'"%(s, cron_file))

        with open(cron_file, 'r') as f:
            new = f.readlines()
            found = False
            for line in new:
                if s == line[:-1]:
                     found = True
                     break
            if not found and e['action'] == 'add':
                raise ComplianceError("wanted cron entry not found: '%s' in '%s'"%(s, cron_file))
            if found and e['action'] == 'del':
                raise ComplianceError("unwanted cron entry found: '%s' in '%s'"%(s, cron_file))

    def _del_cron(self, e):
        cron_file = self.get_cron_file(e)

        if cron_file is None:
            raise Unfixable("no crontab usual location found (%s)"%str(self.crontab_locs))

        s = self.format_entry(cron_file, e)

        if not os.path.exists(cron_file):
            return

        new = []
        with open(cron_file, 'r') as f:
            lines = f.readlines()
            for line in lines:
                if s == line[:-1]:
                    pinfo("delete entry '%s' from '%s'"%(s, cron_file))
                    continue
                new.append(line)

        if len(new) == 0:
            pinfo('deleted last entry of %s. delete file too.'%cron_file)
            os.unlink(cron_file)
        else:
            with open(cron_file, 'w') as f:
                f.write(''.join(new))
            self.activate_cron(cron_file)

    def _add_cron(self, e):
        cron_file = self.get_cron_file(e)

        if cron_file is None:
            raise Unfixable("no crontab usual location found (%s)"%str(self.crontab_locs))

        s = self.format_entry(cron_file, e)

        new = False
        if os.path.exists(cron_file):
            with open(cron_file, 'r') as f:
                new = f.readlines()
                found = False
                for line in new:
                    if s == line[:-1]:
                        found = True
                        break
                if not found:
                    new.append(s+'\n')
        else:
            new = [s+'\n']

        if not new:
            raise ComplianceError("problem preparing the new crontab '%s'"%cron_file)

        pinfo("add entry '%s' to '%s'"%(s, cron_file))
        with open(cron_file, 'w') as f:
            f.write(''.join(new))
        self.activate_cron(cron_file)

if __name__ == "__main__":
    main(CompCron)

  0707010005ca24000081fd0000000000000000000000015ab2175400001a42000000660000000000000000000000000000003900000000root/var/lib/opensvc/compliance/com.opensvc/etcsystem.py  #!/usr/bin/env python

data = {
  "default_prefix": "OSVC_COMP_ETCSYSTEM_",
  "example_value": """ [{"key": "fcp:fcp_offline_delay", "op": ">=", "value": 21}, {"key": "ssd:ssd_io_time", "op": "=", "value": "0x3C"}] """,
  "description": "Checks and setup values in /etc/system respecting strict targets or thresholds.",
  "form_definition": """
Desc: |
  A rule to set a list of Solaris kernel parameters to be set in /etc/system. Current values can be checked as strictly equal, or superior/inferior to their target value.
Css: comp48

Outputs:
  -
    Dest: compliance variable
    Type: json
    Format: list of dict
    Class: etcsystem

Inputs:
  -
    Id: key
    Label: Key
    DisplayModeLabel: key
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The /etc/system parameter to check.

  -
    Id: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string or integer
    Help: The /etc/system parameter target value.

  -
    Id: op
    Label: Comparison operator
    DisplayModeLabel: op
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Default: "="
    Candidates:
      - "="
      - ">"
      - ">="
      - "<"
      - "<="
    Help: The comparison operator to use to check the parameter current value.
""",
}

import os
import sys
from subprocess import *

sys.path.append(os.path.dirname(__file__))

from comp import *

class EtcSystem(CompObject):
    def __init__(self, prefix=None):
        CompObject.__init__(self, prefix=prefix, data=data)

    def init(self):
        self.keys = self.get_rules()
        if len(self.keys) == 0:
            raise NotApplicable()

        self.data = {}
        self.cf = os.path.join(os.sep, 'etc', 'system')
        self.load_file(self.cf)

    def fixable(self):
        return RET_OK

    def load_file(self, p):
        if not os.path.exists(p):
            perror(p, "does not exist")
            return
        with open(p, 'r') as f:
            buff = f.read()
        self.lines = buff.split('\n')
        for i, line in enumerate(self.lines):
            line = line.strip()
            if line.startswith('*'):
                continue
            if len(line) == 0:
                continue
            l = line.split()
            if l[0] != "set":
                continue
            if len(l) < 2:
                continue
            line = ' '.join(l[1:]).split('*')[0]
            var, val = line.split('=')
            var = var.strip()
            val = val.strip()
            try:
                val = int(val)
            except:
                pass
            if var in self.data:
                self.data[var].append([val, i])
            else:
                self.data[var] = [[val, i]]

    def set_val(self, keyname, target, op):
        newline = 'set %s = %s'%(keyname, str(target))
        if keyname not in self.data:
            pinfo("add '%s' to /etc/system"%newline)
            self.lines.insert(-1, newline + " * added by opensvc")
        else:
            ok = 0
            for value, ref in self.data[keyname]:
                r = self._check_key(keyname, target, op, value, ref, verbose=False)
                if r == RET_ERR:
                    pinfo("comment out line %d: %s"%(ref, self.lines[ref]))
                    self.lines[ref] = '* '+self.lines[ref]+' * commented out by opensvc'
                else:
                    ok += 1
            if ok == 0:
                pinfo("add '%s' to /etc/system"%newline)
                self.lines.insert(-1, newline + " * added by opensvc")

    def get_val(self, keyname):
        if keyname not in self.data:
            return []
        return self.data[keyname]

    def _check_key(self, keyname, target, op, value, ref, verbose=True):
        r = RET_OK
        if value is None:
            if verbose:
                perror("%s not set"%keyname)
            r |= RET_ERR
        if op == '=':
            if str(value) != str(target):
                if verbose:
                    perror("%s=%s, target: %s"%(keyname, str(value), str(target)))
                r |= RET_ERR
            elif verbose:
                pinfo("%s=%s on target"%(keyname, str(value)))
        else:
            if type(value) != int:
                if verbose:
                    perror("%s=%s value must be integer"%(keyname, str(value)))
                r |= RET_ERR
            elif op == '<=' and value > target:
                if verbose:
                    perror("%s=%s target: <= %s"%(keyname, str(value), str(target)))
                r |= RET_ERR
            elif op == '>=' and value < target:
                if verbose:
                    perror("%s=%s target: >= %s"%(keyname, str(value), str(target)))
                r |= RET_ERR
            elif verbose:
                pinfo("%s=%s on target"%(keyname, str(value)))
        return r

    def check_key(self, key, verbose=True):
        if 'key' not in key:
            if verbose:
                perror("'key' not set in rule %s"%str(key))
            return RET_NA
        if 'value' not in key:
            if verbose:
                perror("'value' not set in rule %s"%str(key))
            return RET_NA
        if 'op' not in key:
            op = "="
        else:
            op = key['op']
        target = key['value']

        if op not in ('>=', '<=', '='):
            if verbose:
                perror("'value' list member 0 must be either '=', '>=' or '<=': %s"%str(key))
            return RET_NA

        keyname = key['key']
        data = self.get_val(keyname)

        if len(data) == 0:
            perror("%s key is not set"%keyname)
            return RET_ERR

        r = RET_OK
        ok = 0
        for value, ref in data:
            r |= self._check_key(keyname, target, op, value, ref, verbose)
            if r == RET_OK:
                ok += 1

        if ok > 1:
            perror("duplicate lines for key %s"%keyname)
            r |= RET_ERR
        return r

    def fix_key(self, key):
        self.set_val(key['key'], key['value'], key['op'])

    def check(self):
        r = 0
        for key in self.keys:
            r |= self.check_key(key, verbose=True)
        return r

    def fix(self):
        for key in self.keys:
            if self.check_key(key, verbose=False) == RET_ERR:
                self.fix_key(key)
        if len(self.keys) > 0:
            self.backup(self.cf)
            try:
                with open(self.cf, 'w') as f:
                    f.write('\n'.join(self.lines))
            except:
                perror("failed to write %s"%self.cf)
                return RET_ERR
        return RET_OK

if __name__ == "__main__":
    main(EtcSystem)
  0707010005ca25000081fd0000000000000000000000015ab2175400003078000000660000000000000000000000000000003400000000root/var/lib/opensvc/compliance/com.opensvc/file.py   #!/usr/bin/env python

data = {
  "default_prefix": "OSVC_COMP_FILE_",
  "example_value": """ 
{
  "path": "/some/path/to/file",
  "fmt": "root@corp.com		%%HOSTNAME%%@corp.com",
  "uid": 500,
  "gid": 500,
}
  """,
  "description": """* Verify and install file content.
* Verify and set file or directory ownership and permission
* Directory mode is triggered if the path ends with /

Special wildcards::

  %%ENV:VARNAME%%	Any environment variable value
  %%HOSTNAME%%		Hostname
  %%SHORT_HOSTNAME%%	Short hostname

""",
  "form_definition": """
Desc: |
  A file rule, fed to the 'files' compliance object to create a directory or a file and set its ownership and permissions. For files, a reference content can be specified or pointed through an URL.
Css: comp48

Outputs:
  -
    Dest: compliance variable
    Class: file
    Type: json
    Format: dict

Inputs:
  -
    Id: path
    Label: Path
    DisplayModeLabel: path
    LabelCss: action16
    Mandatory: Yes
    Help: File path to install the reference content to. A path ending with '/' is treated as a directory and as such, its content need not be specified.
    Type: string

  -
    Id: mode
    Label: Permissions
    DisplayModeLabel: perm
    LabelCss: action16
    Help: "In octal form. Example: 644"
    Type: integer

  -
    Id: uid
    Label: Owner
    DisplayModeLabel: uid
    LabelCss: guy16
    Help: Either a user ID or a user name
    Type: string or integer

  -
    Id: gid
    Label: Owner group
    DisplayModeLabel: gid
    LabelCss: guy16
    Help: Either a group ID or a group name
    Type: string or integer

  -
    Id: ref
    Label: Content URL pointer
    DisplayModeLabel: ref
    LabelCss: loc
    Help: "Examples:
        http://server/path/to/reference_file
        https://server/path/to/reference_file
        ftp://server/path/to/reference_file
        ftp://login:pass@server/path/to/reference_file"
    Type: string

  -
    Id: fmt
    Label: Content
    DisplayModeLabel: fmt
    LabelCss: hd16
    Css: pre
    Help: A reference content for the file. The text can embed substitution variables specified with %%ENV:VAR%%.
    Type: text
"""
}

import os
import sys
import json
import stat
import re
import urllib
import ssl
import tempfile
import pwd
import grp
from subprocess import *

sys.path.append(os.path.dirname(__file__))

from comp import *

class InitError(Exception):
    pass

class CompFiles(CompObject):
    def __init__(self, prefix=None):
        CompObject.__init__(self, prefix=prefix, data=data)

    def init(self):
        self._usr = {}
        self._grp = {}
        self.sysname, self.nodename, x, x, self.machine = os.uname()
        self.files = []

        for rule in self.get_rules():
            try:
                self.files += self.add_file(rule)
            except InitError:
                continue
            except ValueError:
                perror('file: failed to parse variable', os.environ[k])

        if len(self.files) == 0:
            raise NotApplicable()

    def parse_fmt(self, d, add_linefeed=True):
        if isinstance(d['fmt'], int):
            d['fmt'] = str(d['fmt'])
        d['fmt'] = d['fmt'].replace('%%HOSTNAME%%', self.nodename)
        d['fmt'] = d['fmt'].replace('%%SHORT_HOSTNAME%%', self.nodename.split('.')[0])
        d['fmt'] = self.subst(d['fmt'])
        if add_linefeed and not d['fmt'].endswith('\n'):
            d['fmt'] += '\n'
        return [d]

    def parse_ref(self, d):
        f = tempfile.NamedTemporaryFile()
        tmpf = f.name
        f.close()
        try:
            self.urlretrieve(d['ref'], tmpf)
        except IOError as e:
            perror("file ref", d['ref'], "download failed:", e)
            raise InitError()
        with open(tmpf, "r") as f:
            d['fmt'] = f.read()
        return self.parse_fmt(d, add_linefeed=False)

    def add_file(self, d):
        if 'path' not in d:
            perror('file: path should be in the dict:', d)
            RET = RET_ERR
            return []
        if 'fmt' not in d and 'ref' not in d and not d['path'].endswith("/"):
            perror('file: fmt or ref should be in the dict:', d)
            RET = RET_ERR
            return []
        if 'fmt' in d and 'ref' in d:
            perror('file: fmt and ref are exclusive:', d)
            RET = RET_ERR
            return []
        try:
            d["uid"] = int(d["uid"])
        except:
            pass
        try:
            d["gid"] = int(d["gid"])
        except:
            pass
        if 'fmt' in d:
            return self.parse_fmt(d)
        if 'ref' in d:
            if not d["ref"].startswith("safe://"):
                return self.parse_ref(d)
        return [d]

    def fixable(self):
        return RET_NA

    def check_file_fmt(self, f, verbose=False):
        if not os.path.exists(f['path']):
            return RET_ERR
        if f['path'].endswith('/'):
            # don't check content if it's a directory
            return RET_OK
        if 'ref' in f and f['ref'].startswith("safe://"):
            return self.check_file_fmt_safe(f, verbose=verbose)
        else:
            return self.check_file_fmt_buffered(f, verbose=verbose)

    def fix_file_fmt_safe(self, f):
        pinfo("file reference %s download to %s" % (f["ref"], f["path"]))
        tmpfname = self.get_safe_file(f["ref"])
        pinfo("file %s content install" % f["path"])
        import shutil
        shutil.copy(tmpfname, f["path"])
        os.unlink(tmpfname)
        return RET_OK

    def check_file_fmt_safe(self, f, verbose=False):
        try:
            data = self.collector_safe_file_get_meta(f["ref"])
        except ComplianceError as e:
            raise ComplianceError(str(e))
        target_md5 = data.get("md5")
        current_md5 = self.md5(f["path"])
        if target_md5 == current_md5:
            if verbose:
                pinfo("file %s md5 verified" % f["path"])
            return RET_OK
        else:
            if verbose:
                perror("file %s content md5 differs from its reference" % f["path"])
            return RET_ERR

    def get_safe_file(self, uuid):
        tmpf = tempfile.NamedTemporaryFile()
        tmpfname = tmpf.name
        tmpf.close()
        try:
            self.collector_safe_file_download(uuid, tmpfname)
        except Exception as e:
            raise ComplianceError("%s: %s" % (uuid, str(e)))
        return tmpfname

    def check_file_fmt_buffered(self, f, verbose=False):
        tmpf = tempfile.NamedTemporaryFile()
        tmpfname = tmpf.name
        tmpf.close()
        with open(tmpfname, 'w') as tmpf:
            tmpf.write(f['fmt'])
        ret = self.check_file_diff(f, tmpfname, verbose=verbose)
        os.unlink(tmpfname)
        return ret

    def check_file_diff(self, f, refpath, verbose=False):
        if "OSVC_COMP_NODES_OS_NAME" in os.environ and os.environ['OSVC_COMP_NODES_OS_NAME'] in ("Linux", "Darwin", "FreeBSD"):
            cmd = ['diff', '-u', f['path'], refpath]
        else:
            cmd = ['diff', f['path'], refpath]
        p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        out = bdecode(out)
        if verbose and len(out) > 0:
            perror(out.strip('\n'))
        if p.returncode != 0:
            return RET_ERR
        return RET_OK

    def check_file_mode(self, f, verbose=False):
        if 'mode' not in f:
            return RET_OK
        try:
            mode = oct(stat.S_IMODE(os.stat(f['path']).st_mode))
        except:
            if verbose: perror("file", f['path'], 'stat() failed')
            return RET_ERR
        mode = str(mode).lstrip("0o")
        target_mode = str(f['mode']).lstrip("0o")
        if mode != target_mode:
            if verbose: perror("file", f['path'], 'mode should be %s but is %s'%(target_mode, mode))
            return RET_ERR
        return RET_OK

    def get_uid(self, uid):
        if uid in self._usr:
            return self._usr[uid]
        tuid = uid
        if is_string(uid):
            try:
                info=pwd.getpwnam(uid)
                tuid = info[2]
                self._usr[uid] = tuid
            except:
                perror("file: user %s does not exist"%uid)
                raise ComplianceError()
        return tuid

    def get_gid(self, gid):
        if gid in self._grp:
            return self._grp[gid]
        tgid = gid
        if is_string(gid):
            try:
                info=grp.getgrnam(gid)
                tgid = info[2]
                self._grp[gid] = tgid
            except:
                perror("file: group %s does not exist"%gid)
                raise ComplianceError()
        return tgid

    def check_file_uid(self, f, verbose=False):
        if 'uid' not in f:
            return RET_OK
        tuid = self.get_uid(f['uid'])
        uid = os.stat(f['path']).st_uid
        if uid != tuid:
            if verbose: perror("file", f['path'], 'uid should be %s but is %s'%(tuid, str(uid)))
            return RET_ERR
        return RET_OK

    def check_file_gid(self, f, verbose=False):
        if 'gid' not in f:
            return RET_OK
        tgid = self.get_gid(f['gid'])
        gid = os.stat(f['path']).st_gid
        if gid != tgid:
            if verbose: perror("file", f['path'], 'gid should be %s but is %s'%(tgid, str(gid)))
            return RET_ERR
        return RET_OK

    def check_file(self, f, verbose=False):
        if not os.path.exists(f['path']):
            perror("file", f['path'], "does not exist")
            return RET_ERR
        r = 0
        r |= self.check_file_fmt(f, verbose)
        r |= self.check_file_mode(f, verbose)
        r |= self.check_file_uid(f, verbose)
        r |= self.check_file_gid(f, verbose)
        if r == 0 and verbose:
            pinfo("file", f['path'], "is ok")
        return r

    def fix_file_mode(self, f):
        if 'mode' not in f:
            return RET_OK
        if self.check_file_mode(f) == RET_OK:
            return RET_OK
        try:
            pinfo("file %s mode set to %s"%(f['path'], str(f['mode'])))
            os.chmod(f['path'], int(str(f['mode']), 8))
        except:
            return RET_ERR
        return RET_OK

    def fix_file_owner(self, f):
        uid = -1
        gid = -1

        if 'uid' not in f and 'gid' not in f:
            return RET_OK
        if 'uid' in f and self.check_file_uid(f) != RET_OK:
            uid = self.get_uid(f['uid'])
        if 'gid' in f and self.check_file_gid(f) != RET_OK:
            gid = self.get_gid(f['gid'])
        if uid == -1 and gid == -1:
            return RET_OK
        try:
            os.chown(f['path'], uid, gid)
        except:
            perror("file %s ownership set to %d:%d failed"%(f['path'], uid, gid))
            return RET_ERR
        pinfo("file %s ownership set to %d:%d"%(f['path'], uid, gid))
        return RET_OK

    def fix_file_fmt(self, f):
        if f['path'].endswith("/") and not os.path.exists(f['path']):
            try:
                pinfo("file: mkdir", f['path'])
                os.makedirs(f['path'])
            except:
                perror("file: failed to create", f['path'])
                return RET_ERR
            return RET_OK
        if self.check_file_fmt(f, verbose=False) == RET_OK:
            return RET_OK

        if 'ref' in f and f['ref'].startswith("safe://"):
            return self.fix_file_fmt_safe(f)

        d = os.path.dirname(f['path'])
        if not os.path.exists(d):
           pinfo("file: mkdir", d)
           os.makedirs(d)
           try:
               os.chown(d, self.get_uid(f['uid']), self.get_gid(f['gid']))
           except Exception as e:
               perror("file:", e)
               pass
        self.backup(f['path'])
        try:
            with open(f['path'], 'w') as fi:
                fi.write(f['fmt'])
        except Exception as e:
            perror("file:", e)
            return RET_ERR
        pinfo("file", f['path'], "rewritten")
        return RET_OK

    def check(self):
        r = 0
        for f in self.files:
            r |= self.check_file(f, verbose=True)
        return r

    def fix(self):
        r = 0
        for f in self.files:
            r |= self.fix_file_fmt(f)
            r |= self.fix_file_mode(f)
            r |= self.fix_file_owner(f)
        return r

if __name__ == "__main__":
    main(CompFiles)

0707010005ca26000081fd0000000000000000000000015ab21754000022cf000000660000000000000000000000000000003700000000root/var/lib/opensvc/compliance/com.opensvc/fileinc.py    #!/usr/bin/env python

data = {
  "default_prefix": "OSVC_COMP_FILEINC_",
  "example_value": """ 
{
 "path": "/tmp/foo",
 "check": ".*some pattern.*",
 "fmt": "full added content with %%HOSTNAME%%@corp.com: some pattern into the file."
}
  """,
  "description": """* Verify file content.
* The collector provides the format with wildcards.
* The module replace the wildcards with contextual values.
* The fmt must match the check pattern

Wildcards:
%%ENV:VARNAME%%		Any environment variable value
%%HOSTNAME%%		Hostname
%%SHORT_HOSTNAME%%	Short hostname

""",
  "form_definition": """
Desc: |
  A fileinc rule, fed to the 'fileinc' compliance object to verify a line matching the 'check' regular expression is present in the specified file.
Css: comp48

Outputs:
  -
    Dest: compliance variable
    Class: fileinc
    Type: json
    Format: dict

Inputs:
  -
    Id: path
    Label: Path
    DisplayModeLabel: path
    LabelCss: hd16
    Mandatory: Yes
    Help: File path to search the matching line into.
    Type: string
  -
    Id: check
    Label: Check regexp
    DisplayModeLabel: check
    LabelCss: action16
    Mandatory: Yes
    Help: A regular expression. Matching the regular expression is sufficent to grant compliancy.
    Type: string
  -
    Id: fmt
    Label: Format
    DisplayModeLabel: fmt
    LabelCss: action16
    Help: The line installed if the check pattern is not found in the file.
    Type: string
  -
    Id: ref
    Label: URL to format
    DisplayModeLabel: ref
    LabelCss: loc
    Help: An URL pointing to a file containing the line installed if the check pattern is not found in the file.
    Type: string

""",
}

import os
import sys
import json
import stat
import re
import urllib
import tempfile
import codecs

sys.path.append(os.path.dirname(__file__))

from comp import *

MAXSZ = 8*1024*1024

class CompFileInc(CompObject):
    def __init__(self, prefix=None):
        CompObject.__init__(self, prefix=prefix, data=data)

    def init(self):
        self.files = {}
        self.ok = {}
        self.checks = []
        self.upds = {}

        self.sysname, self.nodename, x, x, self.machine = os.uname()
        for rule in self.get_rules():
            self.add_rule(rule)

        if len(self.checks) == 0:
            raise NotApplicable()

    def fixable(self):
        return RET_NA

    def parse_fmt(self, x):
        if isinstance(x, int):
            x = str(x)
        x = x.replace('%%HOSTNAME%%', self.nodename)
        x = x.replace('%%SHORT_HOSTNAME%%', self.nodename.split('.')[0])
        return x

    def parse_ref(self, url):
        f = tempfile.NamedTemporaryFile()
        tmpf = f.name
        try:
            self.urlretrieve(url, tmpf)
            f.close()
        except Exception as e:
             perror(url, "download error:", e)
             return ''
        content = unicode(f.read())
        return self.parse_fmt(content)

    def read_file(self, path):
        if not os.path.exists(path):
            return ''
        out = ''
        try :
            f = codecs.open(path, 'r', encoding="utf8", errors="ignore")
            out = f.read().rstrip('\n')
            f.close()
        except IOError as e:
            pinfo("cannot read '%s', error=%d - %s" %(path, e.errno, str(e)))
            raise
        except:
            perror("Cannot open '%s', unexpected error: %s"%(path, sys.exc_info()[0]))
            raise
        return out

    def add_rule(self, d):
        r = RET_OK
        if 'path' not in d:
            perror("'path' should be defined:", d)
            r |= RET_ERR
        if 'fmt' in d and 'ref' in d:
            perror("'fmt' and 'ref' are exclusive:", d)
            r |= RET_ERR
        if 'path' in d:
            d['path'] = d['path'].strip()
        if 'ref' in d:
            d['ref'] = d['ref'].strip()
        if not d['path'] in self.upds:
            self.upds[d['path']] = 0
        if not d['path'] in self.files:
            try:
                fsz = os.path.getsize(d['path'])
            except:
                fsz = 0
            if fsz > MAXSZ:
                self.ok[d['path']] = 0
                self.files[d['path']] = ''
                perror("file '%s' is too large [%.2f Mb] to fit" %(d['path'], fsz/(1024.*1024)))
                r |= RET_ERR
            else:
                try:
                    self.files[d['path']] = self.read_file(d['path'])
                    self.ok[d['path']] = 1
                except:
                    self.files[d['path']] = ""
                    self.ok[d['path']] = 0
                    r |= RET_ERR
        c = ''
        if 'fmt' in d:
            c = self.parse_fmt(d['fmt'])
        elif 'ref' in d:
            c = self.parse_ref(d['ref'])
        else:
            perror("'fmt' or 'ref' should be defined:", d)
            r |= RET_ERR
        c = c.strip()
        if re.match(d['check'], c) is not None or len(c) == 0:
            val = True
        else:
            val = False
            r |= RET_ERR
        self.checks.append({'check':d['check'], 'path':d['path'], 'add':c, 'valid':val})
        return r
        
    def check(self):
        r = RET_OK
        for ck in self.checks:
            if not ck['valid']:
                perror("rule error: '%s' does not match target content" % ck['check'])
                r |= RET_ERR
                continue
            if self.ok[ck['path']] != 1:
                r |= RET_ERR
                continue
            pr = RET_OK
            m = 0
            ok = 0
            lines = self.files[ck['path']].split('\n')
            for line in lines:
                if re.match(ck['check'], line):
                    m += 1
                    if len(ck['add']) > 0 and line == ck['add']:
                        pinfo("line '%s' found in '%s'" %(line, ck['path']))
                        ok += 1
                    if m > 1:
                        perror("duplicate match of pattern '%s' in '%s'"%(ck['check'], ck['path']))
                        pr |= RET_ERR
            if len(ck['add']) == 0:
                if m > 0:
                    perror("pattern '%s' found in %s"%(ck['check'], ck['path']))
                    pr |= RET_ERR
                else:
                    pinfo("pattern '%s' not found in %s"%(ck['check'], ck['path']))
            elif ok == 0:
                perror("line '%s' not found in %s"%(ck['add'], ck['path']))
                pr |= RET_ERR
            elif m == 0:
                perror("pattern '%s' not found in %s"%(ck['check'], ck['path']))
                pr |= RET_ERR
            r |= pr
        return r

    def rewrite_files(self):
        r = RET_OK
        for path in self.files:
            if self.upds[path] == 0:
                continue
            if self.ok[path] != 1:
                r |= RET_ERR
                continue
            if not os.path.exists(path):
                perror("'%s' will be created, please check owner and permissions" %path)
            self.backup(path)
            try:
                f = codecs.open(path, 'w', encoding="utf8")
                f.write(self.files[path])
                f.close()
                pinfo("'%s' successfully rewritten" %path)
            except:
                perror("failed to rewrite '%s'" %path)
                r |= RET_ERR
        return r

    def fix(self):
        r = RET_OK
        for ck in self.checks:
            if not ck['valid']:
                perror("rule error: '%s' does not match target content" % ck['check'])
                r |= RET_ERR
                continue
            if self.ok[ck['path']] != 1:
                r |= RET_ERR
                continue
            need_rewrite = False
            m = 0
            lines = self.files[ck['path']].rstrip('\n').split('\n')
            for i, line in enumerate(lines):
                if re.match(ck['check'], line):
                    m += 1
                    if m == 1:
                        if line != ck['add']:
                            # rewrite line
                            pinfo("rewrite %s:%d:'%s', new content: '%s'" %(ck['path'], i, line, ck['add']))
                            lines[i] = ck['add']
                            need_rewrite = True
                    elif m > 1:
                        # purge dup
                        pinfo("remove duplicate line %s:%d:'%s'" %(ck['path'], i, line))
                        lines[i] = ""
                        need_rewrite = True
            if m == 0 and len(ck['add']) > 0:
                pinfo("add line '%s' to %s"%(ck['add'], ck['path']))
                lines.append(ck['add'])
                need_rewrite = True

            if need_rewrite:
                self.files[ck['path']] = '\n'.join(lines).rstrip("\n")+"\n"
                self.upds[ck['path']] = 1

        r |= self.rewrite_files()
        return r


if __name__ == "__main__":
    main(CompFileInc)
 0707010005ca27000081fd0000000000000000000000015ab217540000259f000000660000000000000000000000000000003800000000root/var/lib/opensvc/compliance/com.opensvc/fileprop.py   #!/usr/bin/env python

data = {
  "default_prefix": "OSVC_COMP_FILEPROP_",
  "example_value": """ 
{
  "path": "/some/path/to/file",
  "mode": "750",
  "uid": 500,
  "gid": 500,
}
  """,
  "description": """* Verify file existance, mode and ownership.
* The collector provides the format with wildcards.
* The module replace the wildcards with contextual values.

In fix() the file is created empty with the right mode & ownership.
""",
  "form_definition": """
Desc: |
  A fileprop rule, fed to the 'fileprop' compliance object to verify the target file ownership and permissions.
Css: comp48
Outputs:
  -
    Dest: compliance variable
    Class: fileprop
    Type: json
    Format: dict
Inputs:
  -
    Id: path
    Label: Path
    DisplayModeLabel: path
    LabelCss: action16
    Mandatory: Yes
    Help: File path to check the ownership and permissions for.
    Type: string
  -
    Id: mode
    Label: Permissions
    DisplayModeLabel: perm
    LabelCss: action16
    Help: "In octal form. Example: 644"
    Type: integer
  -
    Id: uid
    Label: Owner
    DisplayModeLabel: uid
    LabelCss: guy16
    Help: Either a user ID or a user name
    Type: string or integer
  -
    Id: gid
    Label: Owner group
    DisplayModeLabel: gid
    LabelCss: guy16
    Help: Either a group ID or a group name
    Type: string or integer
""",
}

import os
import sys
import json
import stat
import re
import pwd
import grp

sys.path.append(os.path.dirname(__file__))

from comp import *

class CompFileProp(CompObject):
    def __init__(self, prefix=None):
        CompObject.__init__(self, prefix=prefix, data=data)

    def init(self):
        self._usr = {}
        self._grp = {}
        self.sysname, self.nodename, x, x, self.machine = os.uname()
        self.files = []

        for rule in self.get_rules():
            try:
                self.files += self.add_file(rule)
            except InitError:
                continue
            except ValueError:
                perror('fileprop: failed to parse variable', os.environ[k])

        if len(self.files) == 0:
            raise NotApplicable()

    def add_file(self, d):
        if 'path' not in d:
            perror('fileprop: path should be in the dict:', d)
            RET = RET_ERR
            return []
        try:
            d["uid"] = int(d["uid"])
        except:
            pass
        try:
            d["gid"] = int(d["gid"])
        except:
            pass
        return [d]

    def fixable(self):
        return RET_NA

    def check_file_type(self, f, verbose=False):
        r = RET_OK
        if not os.path.exists(f["path"].rstrip("/")):
            if verbose: perror("fileprop:", f["path"], "does not exist")
            r = RET_ERR
        elif f["path"].endswith("/") and not os.path.isdir(f["path"]):
            if verbose: perror("fileprop:", f["path"], "exists but is not a directory")
            r = RET_ERR
        elif not f["path"].endswith("/") and os.path.isdir(f["path"]):
            if verbose: perror("fileprop:", f["path"], "exists but is a directory")
            r = RET_ERR
        return r  

    def check_file_mode(self, f, verbose=False):
        if 'mode' not in f:
            return RET_OK
        try:
            mode = oct(stat.S_IMODE(os.stat(f['path']).st_mode))
        except:
            if verbose: perror("fileprop:", f['path'], 'can not stat file')
            return RET_ERR
        mode = str(mode).lstrip("0")
        if mode != str(f['mode']):
            if verbose: perror("fileprop:", f['path'], 'mode should be %s but is %s'%(f['mode'], mode))
            return RET_ERR
        return RET_OK

    def get_uid(self, uid):
        if uid in self._usr:
            return self._usr[uid]
        tuid = uid
        if isinstance(uid, (str, unicode)):
            try:
                info=pwd.getpwnam(uid)
                tuid = info[2]
                self._usr[uid] = tuid
            except:
                perror("fileprop:", "user %s does not exist"%uid)
                raise ComplianceError()
        return tuid

    def get_gid(self, gid):
        if gid in self._grp:
            return self._grp[gid]
        tgid = gid
        if isinstance(gid, (str, unicode)):
            try:
                info=grp.getgrnam(gid)
                tgid = info[2]
                self._grp[gid] = tgid
            except:
                perror("fileprop:",  "group %s does not exist"%gid)
                raise ComplianceError()
        return tgid

    def check_file_uid(self, f, verbose=False):
        if 'uid' not in f:
            return RET_OK
        tuid = self.get_uid(f['uid'])
        try:
            uid = os.stat(f['path']).st_uid
        except:
            if verbose: perror("fileprop:", f['path'], 'can not stat file')
            return RET_ERR
        if uid != tuid:
            if verbose: perror("fileprop:", f['path'], 'uid should be %s but is %s'%(tuid, str(uid)))
            return RET_ERR
        return RET_OK

    def check_file_gid(self, f, verbose=False):
        if 'gid' not in f:
            return RET_OK
        tgid = self.get_gid(f['gid'])
        try:
            gid = os.stat(f['path']).st_gid
        except:
            if verbose: perror("fileprop:", f['path'], 'can not stat file')
            return RET_ERR
        if gid != tgid:
            if verbose: perror("fileprop:", f['path'], 'gid should be %s but is %s'%(tgid, str(gid)))
            return RET_ERR
        return RET_OK

    def check_file_exists(self, f):
        if not os.path.exists(f['path']):
            return RET_ERR
        return RET_OK

    def check_file(self, f, verbose=False):
        if self.check_file_type(f, verbose) == RET_ERR:
            return RET_ERR
        r = 0
        r |= self.check_file_mode(f, verbose)
        r |= self.check_file_uid(f, verbose)
        r |= self.check_file_gid(f, verbose)
        if r == 0 and verbose:
            pinfo("fileprop:", f['path'], "is ok")
        return r

    def fix_file_mode(self, f):
        if 'mode' not in f:
            return RET_OK
        if self.check_file_mode(f) == RET_OK:
            return RET_OK
        try:
            pinfo("fileprop:", "%s mode set to %s"%(f['path'], str(f['mode'])))
            os.chmod(f['path'], int(str(f['mode']), 8))
        except:
            return RET_ERR
        return RET_OK

    def fix_file_owner(self, f):
        uid = -1
        gid = -1

        if 'uid' not in f and 'gid' not in f:
            return RET_OK
        if 'uid' in f and self.check_file_uid(f) != RET_OK:
            uid = self.get_uid(f['uid'])
        if 'gid' in f and self.check_file_gid(f) != RET_OK:
            gid = self.get_gid(f['gid'])
        if uid == -1 and gid == -1:
            return RET_OK
        try:
            os.chown(f['path'], uid, gid)
        except:
            perror("fileprop:", "failed to set %s ownership to %d:%d"%(f['path'], uid, gid))
            return RET_ERR
        pinfo("fileprop:", "%s ownership set to %d:%d"%(f['path'], uid, gid))
        return RET_OK

    def fix_file_notexists(self, f):
        if not os.path.exists(f['path'].rstrip("/")):
            if f['path'].endswith("/"):
                try:
                    os.makedirs(f['path'])
                    pinfo("fileprop:", f['path'], "created")
                except:
                    perror("fileprop:", "failed to create", f['path'])
                    return RET_ERR
                return RET_OK
            else:
                dirname = os.path.dirname(f['path'])
                if not os.path.exists(dirname):
                    pinfo("fileprop:", "create", dirname)
                    try:
                        os.makedirs(dirname)
                    except Exception as e:
                        perror("fileprop:", "failed to create", dirname)
                        return RET_ERR
                pinfo("fileprop:", "touch", f['path'])
                open(f['path'], 'a').close()
        elif f['path'].endswith("/") and not os.path.isdir(f['path']):
                pinfo("fileprop:", "delete file", f['path'].rstrip("/"))
                try:
                    os.unlink(f['path'].rstrip("/"))
                except Exception as e:
                    perror("fileprop:", e)
                    return RET_ERR
                pinfo("fileprop:", "make directory", f['path'])
                try:
                    os.makedirs(f['path'])
                except Exception as e:
                    perror("fileprop:", e)
                    return RET_ERR
        elif not f['path'].endswith("/") and os.path.isdir(f['path']):
            perror("fileprop:", "cowardly refusing to remove the existing", f['path'], "directory to create a regular file")
            return RET_ERR

        if self.check_file_exists(f) == RET_OK:
            return RET_OK
        d = os.path.dirname(f['path'])
        if not os.path.exists(d):
           os.makedirs(d)
           try:
               os.chown(d, f['uid'], f['gid'])
           except:
               pass
        try:
            with open(f['path'], 'w') as fi:
                fi.write('')
        except:
            return RET_ERR
        pinfo("fileprop:", f['path'], "created")
        return RET_OK

    def check(self):
        r = 0
        for f in self.files:
            r |= self.check_file(f, verbose=True)
        return r

    def fix(self):
        r = 0
        for f in self.files:
            r |= self.fix_file_notexists(f)
            r |= self.fix_file_mode(f)
            r |= self.fix_file_owner(f)
        return r


if __name__ == "__main__":
    main(CompFileProp)

 0707010005ca28000081fd0000000000000000000000015ab2175400001a33000000660000000000000000000000000000003800000000root/var/lib/opensvc/compliance/com.opensvc/firmware.py   #!/usr/bin/env python

import os
import sys
import json
from distutils.version import LooseVersion as V
from subprocess import *

sys.path.append(os.path.dirname(__file__))

from comp import *

class CompFirmware(object):
    def __init__(self, var):
        self.versions = {}

        if var not in os.environ:
            pinfo(var, 'not found in environment')
            raise NotApplicable()

        try:
            self.target_versions = json.loads(os.environ[var])
        except:
            perror(var, 'misformatted variable:', os.environ[var])
            raise NotApplicable()
        for key in self.target_versions:
            if type(self.target_versions[key]) != list:
                continue
            self.target_versions[key] = list(map(lambda x: str(x), self.target_versions[key]))

        self.sysname, self.nodename, x, x, self.machine = os.uname()

        if self.sysname not in ['Linux']:
            perror('module not supported on', self.sysname)
            raise NotApplicable()

    def get_versions(self):
        self.get_bios_version_Linux()
        self.get_qla_version_Linux()
        self.get_lpfc_version_Linux()

    def get_qla_version_Linux(self):
        self.versions['qla2xxx'] = None
        self.versions['qla2xxx_fw'] = None
        import glob
        hosts = glob.glob('/sys/bus/pci/drivers/qla2*/*:*:*/host*')
        if len(hosts) == 0:
            return
        hosts_proc = map(lambda x: '/proc/scsi/qla2xxx/'+os.path.basename(x).replace('host', ''), hosts)
        hosts = map(lambda x: '/sys/class/fc_host/'+os.path.basename(x)+'/symbolic_name', hosts)
        for i, host in enumerate(hosts):
            if os.path.exists(host):
                with open(host, 'r') as f:
                    buff = f.read()
                l = buff.split()
                for e in l:
                    if e.startswith("DVR:"):
                        self.versions['qla2xxx'] = e.replace("DVR:", "")
                    elif e.startswith("FW:"):
                        v = e.replace("FW:", "")
                        # store the lowest firmware version
                        if self.versions['qla2xxx_fw'] is None or V(self.versions['qla2xxx_fw']) > V(v):
                            self.versions['qla2xxx_fw'] = v
            elif os.path.exists(hosts_proc[i]):
                with open(hosts_proc[i], 'r') as f:
                    buff = f.read()
                for line in buff.split('\n'):
                    if "Firmware version" not in line:
                        continue
                    l = line.split()
                    n_words = len(l)
                    idx = l.index("Driver") + 2
                    if idx <= n_words:
                        self.versions['qla2xxx'] = l[idx]
                    idx = l.index("Firmware") + 2
                    if idx <= n_words:
                        v = l[idx]
                        if self.versions['qla2xxx_fw'] is None or V(self.versions['qla2xxx_fw']) > V(v):
                            self.versions['qla2xxx_fw'] = v

    def get_lpfc_version_Linux(self):
        self.versions['lpfc'] = None
        self.versions['lpfc_fw'] = None
        import glob
        hosts = glob.glob('/sys/class/scsi_host/host*/fwrev')
        if len(hosts) == 0:
            return
        for host in hosts:
            with open(host, 'r') as f:
                buff = f.read()
            l = buff.split()
            if self.versions['lpfc_fw'] is None or V(self.versions['lpfc_fw']) > V(l[0]):
                self.versions['lpfc_fw'] = l[0]

        if self.versions['lpfc_fw'] is None:
            # no need to fetch module version if no hardware
            return

        cmd = ['modinfo', 'lpfc']
        p = Popen(cmd, stdout=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            return
        out = bdecode(out)
        for line in out.splitlines():
            if line.startswith('version:'):
                self.versions['lpfc'] = line.split()[1]
                return

    def get_bios_version_Linux(self):
        p = os.path.join(os.sep, 'sys', 'class', 'dmi', 'id', 'bios_version')
        try:
            f = open(p, 'r')
            ver = f.read().strip()
            f.close()
            self.versions['server'] = ver
            return
        except:
            pass

        try:
            cmd = ['dmidecode']
            p = Popen(cmd, stdout=PIPE, stderr=PIPE)
            out, err = p.communicate()
            if p.returncode != 0:
                raise
            out = bdecode(out)
            for line in out.splitlines():
                if 'Version:' in line:
                    self.versions['server'] = line.split(':')[-1].strip()
                    return
            raise
        except:
            pinfo('can not fetch bios version')
            return

    def fixable(self):
        return RET_NA

    def check(self):
        self.get_versions()
        r = RET_OK
        for key in self.target_versions:
            if key not in self.versions:
                perror("TODO: get", key, "version")
                continue
            if type(self.versions[key]) not in (str, unicode):
                pinfo("no", key)
                continue
            if type(self.target_versions[key]) == list and \
               self.versions[key] not in self.target_versions[key]:
                perror(key, "version is %s, target %s"%(self.versions[key], ' or '.join(self.target_versions[key])))
                r |= RET_ERR
            elif type(self.target_versions[key]) != list and \
                 self.versions[key] != self.target_versions[key]:
                perror(key, "version is %s, target %s"%(self.versions[key], self.target_versions[key]))
                r |= RET_ERR
            else:
                pinfo(key, "version is %s, on target"%self.versions[key])
                continue
        return r

    def fix(self):
        return RET_NA

if __name__ == "__main__":
    syntax = """syntax:
      %s TARGET check|fixable|fix"""%sys.argv[0]
    if len(sys.argv) != 3:
        perror("wrong number of arguments")
        perror(syntax)
        sys.exit(RET_ERR)
    try:
        o = CompFirmware(sys.argv[1])
        if sys.argv[2] == 'check':
            RET = o.check()
        elif sys.argv[2] == 'fix':
            RET = o.fix()
        elif sys.argv[2] == 'fixable':
            RET = o.fixable()
        else:
            perror("unsupported argument '%s'"%sys.argv[2])
            perror(syntax)
            RET = RET_ERR
    except NotApplicable:
        sys.exit(RET_NA)
    except:
        import traceback
        traceback.print_exc()
        sys.exit(RET_ERR)

    sys.exit(RET)

 0707010005ca29000081fd0000000000000000000000015ab2175400005801000000660000000000000000000000000000003200000000root/var/lib/opensvc/compliance/com.opensvc/fs.py #!/usr/bin/env python
""" 
Verify file content. The collector provides the format with
wildcards. The module replace the wildcards with contextual
values.

The variable format is json-serialized:

[{
 "dev": "lv_applisogm",
 "size": "1024M",
 "mnt": "/%%ENV:SVCNAME%%/applis/ogm",
 "vg": ["%%ENV:SVCNAME%%", "vgAPPLIS", "vgCOMMUN01", "vgLOCAL"]
}]

Wildcards:
%%ENV:VARNAME%%		Any environment variable value

Toggle:
%%ENV:FS_STRIP_SVCNAME_FROM_DEV_IF_IN_VG%%

"""

import os
import sys
import json
import stat
import re
from subprocess import *
from stat import *

sys.path.append(os.path.dirname(__file__))

from comp import *
from utilities import which

class CompFs(object):
    def __init__(self, prefix='OSVC_COMP_FS_'):
        self.prefix = prefix.upper()
        self.sysname, self.nodename, x, x, self.machine = os.uname()
        self.sysname = self.sysname.replace('-', '')
        self.fs = []
        self.res = {}
        self.res_status = {}

        if 'OSVC_COMP_SERVICES_SVCNAME' in os.environ:
            self.svcname = os.environ['OSVC_COMP_SERVICES_SVCNAME']
            self.osvc_service = True
        else:
            os.environ['OSVC_COMP_SERVICES_SVCNAME'] = ""
            self.svcname = None
            self.osvc_service = False

        keys = [key for key in os.environ if key.startswith(self.prefix)]
        if len(keys) == 0:
            raise NotApplicable()

        self.vglist()

        for k in keys:
            try:
                self.fs += self.add_fs(os.environ[k])
            except ValueError:
                perror('failed to parse variable', os.environ[k])

        if len(self.fs) == 0:
            raise NotApplicable()

        self.fs.sort(lambda x, y: cmp(x['mnt'], y['mnt']))


    def vglist_HPUX(self):
        import glob
        l = glob.glob("/dev/*/group")
        l = map(lambda x: x.split('/')[2], l)
        self.vg = l

    def vglist_Linux(self):
        if not which("vgs"):
            perror('vgs command not found')
            raise ComplianceError()
        cmd = ['vgs', '-o', 'vg_name', '--noheadings']
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            perror('failed to list volume groups')
            raise ComplianceError()
        out = bdecode(out)
        self.vg = out.split()

    def vglist(self):
        if not hasattr(self, 'vglist_'+self.sysname):
            perror(self.sysname, 'not supported')
            raise NotApplicable()
        getattr(self, 'vglist_'+self.sysname)()
        
    def subst(self, v):
        if type(v) == list:
            l = []
            for _v in v:
                l.append(self.subst(_v))
            return l
        if type(v) != str and type(v) != unicode:
            return v

        p = re.compile('%%ENV:\w+%%')
        for m in p.findall(v):
            s = m.strip("%").replace('ENV:', '')
            if s in os.environ:
                _v = os.environ[s]
            elif 'OSVC_COMP_'+s in os.environ:
                _v = os.environ['OSVC_COMP_'+s]
            else:
                perror(s, 'is not an env variable')
                raise NotApplicable()
            v = v.replace(m, _v)
        return v.strip()

    def add_fs(self, v):
        if type(v) == str or type(v) == unicode:
            d = json.loads(v)
        else:
            d = v
        l = []

        # recurse if multiple fs are specified in a list of dict
        if type(d) == list:
            for _d in d:
                l += self.add_fs(_d)
            return l

        if type(d) != dict:
            perror("not a dict:", d)
            return l

        if 'dev' not in d:
            perror('dev should be in the dict:', d)
            return l
        if 'mnt' not in d:
            perror('mnt should be in the dict:', d)
            return l
        if 'size' not in d:
            perror('size should be in the dict:', d)
            return l
        if 'vg' not in d:
            perror('vg should be in the dict:', d)
            return l
        if 'type' not in d:
            perror('type should be in the dict:', d)
            return l
        if 'opts' not in d:
            perror('opts should be in the dict:', d)
            return l
        if type(d['vg']) != list:
            d['vg'] = [d['vg']]

        d['vg_orig'] = d['vg']
        d['vg'] = self.subst(d['vg'])
        d['prefvg'] = self.prefvg(d)
        d['dev'] = self.strip_svcname(d)

        for k in ('dev', 'mnt', 'size', 'type', 'opts'):
            d[k] = self.subst(d[k])

        d['mnt'] = self.normpath(d['mnt'])
        d['devpath'] = self.devpath(d)
        d['rdevpath'] = self.rdevpath(d)
        try:
            d['size'] = self.size_to_mb(d)
        except ComplianceError:
            return []

        return [d]

    def strip_svcname(self, fs):
        key = "OSVC_COMP_FS_STRIP_SVCNAME_FROM_DEV_IF_IN_VG"
        if key not in os.environ or os.environ[key] != "true":
            return fs['dev']
        if "%%ENV:SERVICES_SVCNAME%%" not in fs['vg_orig'][fs['prefvg_idx']]:
            return fs['dev']

        # the vg is dedicated to the service. no need to embed
        # the service name in the lv name too
        s = fs['dev'].replace("%%ENV:SERVICES_SVCNAME%%", "")
        if s == "lv_":
            s = "root"
        return s

    def normpath(self, p):
        l = p.split('/')
        p = os.path.normpath(os.path.join(os.sep, *l))
        return p

    def rdevpath(self, d):
        return '/dev/%s/r%s'%(d['prefvg'], d['dev'])

    def devpath(self, d):
        return '/dev/%s/%s'%(d['prefvg'], d['dev'])

    def prefvg(self, d):
        lc_candidate_vg = map(lambda x: x.lower(), d['vg'])
        lc_existing_vg = map(lambda x: x.lower(), self.vg)
        for i, vg in enumerate(lc_candidate_vg):
            if vg in lc_existing_vg:
                d['prefvg_idx'] = i
                # return capitalized vg name
                return self.vg[lc_existing_vg.index(vg)]
        perror("no candidate vg is available on this node for dev %s"%d['dev'])
        raise NotApplicable()

    def check_fs_mnt(self, fs, verbose=False):
        if not os.path.exists(fs['mnt']):
            if verbose:
                perror("mount point", fs['mnt'], "does not exist")
            return 1
        if verbose:
            pinfo("mount point", fs['mnt'], "exists")
        return 0

    def check_fs_dev_exists(self, fs, verbose=False):
        if not os.path.exists(fs['devpath']):
            if verbose:
                perror("device", fs['devpath'], "does not exist")
            return 1
        if verbose:
            pinfo("device", fs['devpath'], "exists")
        return 0

    def check_fs_dev_stat(self, fs, verbose=False):
        mode = os.stat(fs['devpath'])[ST_MODE]
        if not S_ISBLK(mode):
            if verbose:
                perror("device", fs['devpath'], "is not a block device")
            return 1
        if verbose:
            pinfo("device", fs['devpath'], "is a block device")
        return 0

    def find_vg_rid(self, vgname):
        rids = [ rid for rid in self.res_status.keys() if rid.startswith('vg#') ]
        for rid in rids:
            if self.get_res_item(rid, 'vgname') == vgname:
                return rid
        return None

    def private_svc_vg_down(self, fs):
        if self.svcname is None or not self.osvc_service:
            return False
        rid = self.find_vg_rid(fs['prefvg'])
        if rid is None:
            # vg is not driven by the service
            return False
        if self.res_status[rid] not in ('up', 'stdby up'):
            return False
        return True

    def check_fs_dev(self, fs, verbose=False):
        if self.private_svc_vg_down(fs):
            # don't report error on passive node with private svc prefvg
            return 0
        if self.check_fs_dev_exists(fs, verbose) == 1:
            return 1
        if self.check_fs_dev_stat(fs, verbose) == 1:
            return 1
        return 0

    def fix_fs_dev(self, fs):
        if self.check_fs_dev(fs, False) == 0:
            return 0
        if self.check_fs_dev_exists(fs, False) == 0:
            perror("device", fs['devpath'], "already exists. won't fix.")
            return 1
        return self.createlv(fs)

    def createlv(self, fs):
        if not hasattr(self, 'createlv_'+self.sysname):
            perror(self.sysname, 'not supported')
            raise NotApplicable()
        return getattr(self, 'createlv_'+self.sysname)(fs)

    def size_to_mb(self, fs):
        s = fs['size']
        unit = s[-1]
        size = int(s[:-1])
        if unit == 'T':
            s = str(size*1024*1024)
        elif unit == 'G':
            s = str(size*1024)
        elif unit == 'M':
            s = str(size)
        elif unit == 'K':
            s = str(size//1024)
        else:
            perror("unknown size unit in rule: %s (use T, G, M or K)"%s)
            raise ComplianceError()
        return s

    def createlv_HPUX(self, fs):
        cmd = ['lvcreate', '-n', fs['dev'], '-L', fs['size'], fs['prefvg']]
        pinfo(' '.join(cmd))
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        out = bdecode(out)
        err = bdecode(err)
        if len(out) > 0:
            pinfo(out)
        if len(err) > 0:
            pinfo(err)
        if p.returncode != 0:
            return 1
        return 0

    def createlv_Linux(self, fs):
        os.environ["LVM_SUPPRESS_FD_WARNINGS"] = "1"
        cmd = ['lvcreate', '-n', fs['dev'], '-L', fs['size']+'M', fs['prefvg']]
        pinfo(' '.join(cmd))
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        out = bdecode(out)
        err = bdecode(err)
        if len(out) > 0:
            pinfo(out)
        if len(err) > 0:
            pinfo(err)
        if p.returncode != 0:
            return 1
        return 0

    def fix_fs_mnt(self, fs, verbose=False):
        if self.check_fs_mnt(fs, False) == 0:
            return 0
        pinfo("create", fs['mnt'], "mount point")
        os.makedirs(fs['mnt'])
        return 0

    def check_fs_fmt_HPUX_vxfs(self, fs, verbose=False):
        cmd = ['fstyp', fs['devpath']]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        out = bdecode(out)
        err = bdecode(err)
        if p.returncode != 0 or "vxfs" not in out:
            if verbose:
                perror(fs['devpath'], "is not formatted")
            return 1
        if verbose:
            pinfo(fs['devpath'], "is correctly formatted")
        return 0

    def check_fs_fmt_HPUX(self, fs, verbose=False):
        if fs['type'] == 'vxfs':
            return self.check_fs_fmt_HPUX_vxfs(fs, verbose)
        perror("unsupported fs type: %s"%fs['type'])
        return 1

    def check_fs_fmt_Linux(self, fs, verbose=False):
        if fs['type'] in ('ext2', 'ext3', 'ext4'):
            return self.check_fs_fmt_Linux_ext(fs, verbose)
        perror("unsupported fs type: %s"%fs['type'])
        return 1

    def check_fs_fmt_Linux_ext(self, fs, verbose=False):
        cmd = ['tune2fs', '-l', fs['devpath']]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        out = bdecode(out)
        err = bdecode(err)
        if p.returncode != 0:
            if verbose:
                perror(fs['devpath'], "is not formatted")
            return 1
        if verbose:
            pinfo(fs['devpath'], "is correctly formatted")
        return 0

    def fix_fs_fmt_Linux_ext(self, fs):
        cmd = ['mkfs.'+fs['type'], '-q', '-b', '4096', fs['devpath']]
        pinfo(' '.join(cmd))
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        out = bdecode(out)
        err = bdecode(err)
        if len(out) > 0:
            pinfo(out)
        if len(err) > 0:
            pinfo(err)
        if p.returncode != 0:
            return 1

        cmd = ['tune2fs', '-m', '0', '-c', '0', '-i', '0', fs['devpath']]
        pinfo(' '.join(cmd))
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        out = bdecode(out)
        err = bdecode(err)
        if len(out) > 0:
            pinfo(out)
        if len(err) > 0:
            pinfo(err)
        if p.returncode != 0:
            return 1

        return 0

    def fix_fs_fmt_Linux(self, fs):
        if fs['type'] in ('ext2', 'ext3', 'ext4'):
            return self.fix_fs_fmt_Linux_ext(fs)
        perror("unsupported fs type: %s"%fs['type'])
        return 1

    def check_fs_fmt(self, fs, verbose=False):
        if not hasattr(self, 'check_fs_fmt_'+self.sysname):
            perror(self.sysname, 'not supported')
            raise NotApplicable()
        return getattr(self, 'check_fs_fmt_'+self.sysname)(fs, verbose)

    def fix_fs_fmt_HPUX_vxfs(self, fs):
        cmd = ['newfs', '-F', 'vxfs', '-b', '8192', fs['rdevpath']]
        pinfo(' '.join(cmd))
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        out = bdecode(out)
        err = bdecode(err)
        if len(out) > 0:
            pinfo(out)
        if len(err) > 0:
            pinfo(err)
        if p.returncode != 0:
            return 1
        return 0

    def fix_fs_fmt_HPUX(self, fs):
        if fs['type'] == 'vxfs':
            return self.fix_fs_fmt_HPUX_vxfs(fs)
        perror("unsupported fs type: %s"%fs['type'])
        return 1

        if not hasattr(self, 'check_fs_fmt_'+self.sysname):
            perror(self.sysname, 'not supported')
            raise NotApplicable()
        return getattr(self, 'check_fs_fmt_'+self.sysname)(fs, verbose)

    def fix_fs_fmt(self, fs):
        if self.check_fs_fmt(fs) == 0:
            return 0
        if not hasattr(self, 'fix_fs_fmt_'+self.sysname):
            perror(self.sysname, 'not supported')
            raise NotApplicable()
        return getattr(self, 'fix_fs_fmt_'+self.sysname)(fs)

    def get_res_item(self, rid, item):
        cmd = ['svcmgr', '-s', self.svcname, 'get', '--param', '.'.join((rid, item))]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        out = bdecode(out)
        err = bdecode(err)
        if p.returncode != 0:
            perror(' '.join(cmd), 'failed')
            return 1
        return out.strip()
 
    def get_res(self, rid):
        if rid in self.res:
            return self.res[rid]
        d = {}
        d['mnt'] = self.get_res_item(rid, 'mnt')
        d['dev'] = self.get_res_item(rid, 'dev')
        self.res[rid] = d
        return d

    def get_fs_rids(self, refresh=False):
        if not refresh and hasattr(self, 'rids'):
            return self.rids
        cmd = ['svcmgr', '-s', self.svcname, 'json_status']
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        out = bdecode(out)
        err = bdecode(err)
        for line in out.splitlines():
            if line.startswith('{'):
                out = line
                break
        try:
            # json_status returns 0, even when it outs no data
            self.res_status = json.loads(out)['resources']
        except Exception as e:
            pinfo(e)
            pinfo(out)
            self.rids = []
            self.osvc_service = False
            return self.rids
        self.rids = [ k for k in self.res_status.keys() if k.startswith('fs#') ]
        return self.rids

    def find_rid(self, fs):
        found = False
        for rid in self.rids:
            d = self.get_res(rid)
            if d['mnt'] == fs['mnt'] and d['dev'] == fs['devpath']:
                return rid
        return None

    def fix_fs_local(self, fs):
        if self.svcname is not None and self.osvc_service:
            return 0
        if self.check_fs_local(fs, False) == 0:
            return 0
        with open("/etc/fstab", "r") as f:
            lines = f.read().split('\n')
        if len(lines[-1]) == 0:
            del(lines[-1])
        p = re.compile(r'\s*%s\s+'%(fs['devpath']))
        newline = "%s %s %s %s 0 2"%(fs['devpath'], fs['mnt'], fs['type'], fs['opts'])
        for i, line in enumerate(lines):
            if line == newline:
                return 0
            if re.match(p, line) is not None:
                pinfo("remove '%s' from fstab"%line)
                del lines[i]
        lines.append(newline)
        pinfo("append '%s' to fstab"%newline)
        try:
            with open("/etc/fstab", "w") as f:
                f.write("\n".join(lines)+'\n')
        except:
            perror("failed to rewrite fstab")
            return 1
        pinfo("fstab rewritten")
        return 0

    def check_fs_local(self, fs, verbose=False):
        if self.svcname is not None and self.osvc_service:
            return 0
        p = re.compile(r'\s*%s\s+%s'%(fs['devpath'], fs['mnt']))
        with open("/etc/fstab", "r") as f:
            buff = f.read()
        if re.search(p, buff) is not None:
            if verbose:
                pinfo("%s@%s resource correctly set in fstab"%(fs['mnt'], fs['devpath']))
                return 0
        if verbose:
            perror("%s@%s resource correctly set in fstab"%(fs['mnt'], fs['devpath']))
        return 1

    def check_fs_svc(self, fs, verbose=False):
        if self.svcname is None:
            return 0
        rids = self.get_fs_rids()
        if not self.osvc_service:
            return 0
        rid = self.find_rid(fs)
        if rid is None:
            if verbose:
                perror("%s@%s resource not found in service %s"%(fs['mnt'], fs['devpath'], self.svcname))
            return 1
        if verbose:
            pinfo("%s@%s resource correctly set in service %s"%(fs['mnt'], fs['devpath'], self.svcname))
        return 0
            
    def fix_fs_svc(self, fs):
        if not self.osvc_service or self.check_fs_svc(fs, False) == 0:
            return 0
        cmd = ['svcmgr', '-s', self.svcname, 'get', '--param', 'DEFAULT.encapnodes']
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        out = bdecode(out)
        err = bdecode(err)
        if self.nodename in out.strip().split():
            tags = "encap"
        else:
            tags = ''
        cmd = ['svcmgr', '-s', self.svcname, 'update', '--resource',
               '{"rtype": "fs", "mnt": "%s", "dev": "%s", "type": "%s", "mnt_opt": "%s", "tags": "%s"}'%(fs['mnt'], fs['devpath'], fs['type'], fs['opts'], tags)]
        pinfo(' '.join(cmd))
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        out = bdecode(out)
        err = bdecode(err)
        if p.returncode != 0:
            perror("unable to fetch %s json status"%self.svcname)
            return 1
        return 0

    def check_fs_mounted(self, fs, verbose=False):
        if os.path.ismount(fs['mnt']):
            if verbose:
                pinfo(fs['mnt'], "is mounted")
            return 0
        if verbose:
            perror(fs['mnt'], "is not mounted")
        return 1

    def fix_fs_mounted(self, fs):
        if self.check_fs_mounted(fs, False) == 0:
            return 0
        if self.svcname is None or not self.osvc_service:
            return self.fix_fs_mounted_local(fs)
        else:
            return self.fix_fs_mounted_svc(fs)

    def fix_fs_mounted_svc(self, fs):
        rids = self.get_fs_rids(refresh=True)
        rid = self.find_rid(fs)
        if rid is None:
            perror("fs resource with mnt=%s not found in service %s"%(fs['mnt'], self.svcname))
            return 1
        cmd = ['svcmgr', '-s', self.svcname, '--rid', rid, 'mount', '--cluster']
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        out = bdecode(out)
        err = bdecode(err)
        if p.returncode != 0 and "unsupported action" in err:
            cmd = ['svcmgr', '-s', self.svcname, '--rid', rid, 'startfs', '--cluster']
            p = Popen(cmd, stdout=PIPE, stderr=PIPE)
            out, err = p.communicate()
        pinfo(' '.join(cmd))
        if p.returncode != 0:
            perror("unable to mount %s"%fs['mnt'])
            return 1
        return 0

    def fix_fs_mounted_local(self, fs):
        cmd = ['mount', fs['mnt']]
        pinfo(' '.join(cmd))
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        out = bdecode(out)
        err = bdecode(err)
        if len(out) > 0:
            pinfo(out)
        if len(err) > 0:
            perror(err)
        if p.returncode != 0:
            perror("unable to mount %s"%fs['mnt'])
            return 1
        return 0

    def check_fs(self, fs, verbose=False):
        r = 0
        r |= self.check_fs_mnt(fs, verbose)
        r |= self.check_fs_dev(fs, verbose)
        r |= self.check_fs_fmt(fs, verbose)
        r |= self.check_fs_svc(fs, verbose)
        r |= self.check_fs_local(fs, verbose)
        r |= self.check_fs_mounted(fs, verbose)
        return r

    def fix_fs(self, fs):
        if self.fix_fs_mnt(fs) != 0:
            return 1
        if self.fix_fs_dev(fs) != 0:
            return 1
        if self.fix_fs_fmt(fs) != 0:
            return 1
        if self.fix_fs_svc(fs) != 0:
            return 1
        if self.fix_fs_local(fs) != 0:
            return 1
        if self.fix_fs_mounted(fs) != 0:
            return 1
        return 0

    def fixable(self):
        return RET_NA

    def check(self):
        r = 0
        for f in self.fs:
            r |= self.check_fs(f, verbose=True)
        return r

    def fix(self):
        r = 0
        for f in self.fs:
            r |= self.fix_fs(f)
        return r


if __name__ == "__main__":
    syntax = """syntax:
      %s PREFIX check|fixable|fix"""%sys.argv[0]
    if len(sys.argv) != 3:
        perror("wrong number of arguments")
        perror(syntax)
        sys.exit(RET_ERR)
    try:
        o = CompFs(sys.argv[1])
        if sys.argv[2] == 'check':
            RET = o.check()
        elif sys.argv[2] == 'fix':
            RET = o.fix()
        elif sys.argv[2] == 'fixable':
            RET = o.fixable()
        else:
            perror("unsupported argument '%s'"%sys.argv[2])
            perror(syntax)
            RET = RET_ERR
    except NotApplicable:
        sys.exit(RET_NA)
    except ComplianceError:
        sys.exit(RET_ERR)
    except:
        import traceback
        traceback.print_exc()
        sys.exit(RET_ERR)

    sys.exit(RET)

   0707010005ca2b000081fd0000000000000000000000015ab2175400001e3a000000660000000000000000000000000000003500000000root/var/lib/opensvc/compliance/com.opensvc/group.py  #!/usr/bin/env python

data = {
  "default_prefix": "OSVC_COMP_GROUP_",
  "example_value": """
{
  "tibco": {
    "gid": 1000,
  },
  "tibco1": {
    "gid": 1001,
  }
}
""",
  "description": """* Verify a local system group configuration
* A minus (-) prefix to the group name indicates the user should not exist

""",
  "form_definition": """
Desc: |
  A rule defining a list of Unix groups and their properties. Used by the groups compliance objects.
Css: comp48
Outputs:
  -
    Dest: compliance variable
    Type: json
    Format: dict of dict
    Key: group
    EmbedKey: No
    Class: group
Inputs:
  -
    Id: group
    Label: Group name
    DisplayModeLabel: group
    LabelCss: guys16
    Mandatory: Yes
    Type: string
    Help: The Unix group name.
  -
    Id: gid
    Label: Group id
    DisplayModeLabel: gid
    LabelCss: guys16
    Type: string or integer
    Help: The Unix gid of this group.
""",
}

import os
import sys
import json
import grp
import re
from subprocess import Popen 

sys.path.append(os.path.dirname(__file__))

from comp import *

blacklist = [
 "root",
 "bin",
 "daemon",
 "sys",
 "adm",
 "tty",
 "disk",
 "lp",
 "mem",
 "kmem",
 "wheel",
 "mail",
 "uucp",
 "man",
 "games",
 "gopher",
 "video",
 "dip",
 "ftp",
 "lock",
 "audio",
 "nobody",
 "users",
 "utmp",
 "utempter",
 "floppy",
 "vcsa",
 "cdrom",
 "tape",
 "dialout",
 "saslauth",
 "postdrop",
 "postfix",
 "sshd",
 "opensvc",
 "mailnull",
 "smmsp",
 "slocate",
 "rpc",
 "rpcuser",
 "nfsnobody",
 "tcpdump",
 "ntp"
]

class CompGroup(CompObject):
    def __init__(self, prefix=None):
        CompObject.__init__(self, prefix=prefix, data=data)

    def init(self):
        self.grt = {
            'gid': 'gr_gid',
        }

        self.groupmod_p = {
            'gid': '-g',
        }

        self.sysname, self.nodename, x, x, self.machine = os.uname()

        if self.sysname == "FreeBSD":
            self.groupadd = ["pw", "groupadd"]
            self.groupmod = ["pw", "groupmod"]
            self.groupdel = ["pw", "groupdel"]
        elif self.sysname == 'AIX':
            self.groupmod = ['chgroup']
            self.groupadd = ['mkgroup']
            self.groupdel = ['rmgroup']
            self.groupmod_p = {
                'gid': 'id',
            }
        else:
            self.groupadd = ["groupadd"]
            self.groupmod = ["groupmod"]
            self.groupdel = ["groupdel"]

        if self.sysname not in ['SunOS', 'Linux', 'HP-UX', 'AIX', 'OSF1', 'FreeBSD']:
            perror('group: module not supported on', self.sysname)
            raise NotApplicable

        self.groups = {}
        for d in self.get_rules():
            self.groups.update(d)

        for group, d in self.groups.items():
            for k in ('uid', 'gid'):
                if k in d:
                    self.groups[group][k] = int(d[k])

    def fixable(self):
        return RET_NA

    def fmt_opt_gen(self, item, target):
        return [item, target]

    def fmt_opt_aix(self, item, target):
        return ['='.join((item, target))]

    def fmt_opt(self, item, target):
        if self.sysname == 'AIX':
            return self.fmt_opt_aix(item, target)
        else:
            return self.fmt_opt_gen(item, target)
        
    def fix_item(self, group, item, target):
        if item in self.groupmod_p:
            cmd = [] + self.groupmod
            if self.sysname == "FreeBSD":
                cmd += [group]
            cmd += self.fmt_opt(self.groupmod_p[item], str(target))
            if self.sysname != "FreeBSD":
                cmd += [group]
            pinfo("group:", ' '.join(cmd))
            p = Popen(cmd)
            out, err = p.communicate()
            r = p.returncode
            if r == 0:
                return RET_OK
            else:
                return RET_ERR
        else:
            perror('group: no fix implemented for', item)
            return RET_ERR

    def check_item(self, group, item, target, current, verbose=False):
        if type(current) == int and current < 0:
            current += 4294967296
        if target == current:
            if verbose:
                pinfo('group', group, item+':', current)
            return RET_OK
        else:
            if verbose:
                perror('group', group, item+':', current, 'target:', target)
            return RET_ERR 

    def try_create_group(self, props):
        #
        # don't try to create group if passwd db is not 'files'
        # beware: 'files' db is the implicit default
        #
        if 'db' in props and props['db'] != 'files':
            return False
        if set(self.grt.keys()) <= set(props.keys()):
            return True
        return False

    def check_group_del(self, group):
        try:
            groupinfo = grp.getgrnam(group)
        except KeyError:
            pinfo('group', group, 'does not exist, on target')
            return RET_OK
        perror('group', group, "exists, shouldn't")
        return RET_ERR

    def check_group(self, group, props):
        if group.startswith('-'):
            return self.check_group_del(group.lstrip('-'))
        r = 0
        try:
            groupinfo = grp.getgrnam(group)
        except KeyError:
            if self.try_create_group(props):
                perror('group', group, 'does not exist')
                return RET_ERR
            else:
                pinfo('group', group, 'does not exist and not enough info to create it')
                return RET_OK
        for prop in self.grt:
            if prop in props:
                r |= self.check_item(group, prop, props[prop], getattr(groupinfo, self.grt[prop]), verbose=True)
        return r

    def create_group(self, group, props):
        cmd = [] + self.groupadd
        if self.sysname == "FreeBSD":
            cmd += [group]
        for item in self.grt:
            cmd += self.fmt_opt(self.groupmod_p[item], str(props[item]))
        if self.sysname != "FreeBSD":
            cmd += [group]
        pinfo("group:", ' '.join(cmd))
        p = Popen(cmd)
        out, err = p.communicate()
        r = p.returncode
        if r == 0:
            return RET_OK
        else:
            return RET_ERR

    def fix_group_del(self, group):
        if group in blacklist:
            perror("group", group, "... cowardly refusing to delete")
            return RET_ERR
        try:
            groupinfo = grp.getgrnam(group)
        except KeyError:
            return RET_OK
        cmd = self.groupdel + [group]
        pinfo("group:", ' '.join(cmd))
        p = Popen(cmd)
        out, err = p.communicate()
        r = p.returncode
        if r == 0:
            return RET_OK
        else:
            return RET_ERR

    def fix_group(self, group, props):
        if group.startswith('-'):
            return self.fix_group_del(group.lstrip('-'))
        r = 0
        try:
            groupinfo = grp.getgrnam(group)
        except KeyError:
            if self.try_create_group(props):
                return self.create_group(group, props)
            else:
                perror('group', group, 'does not exist')
                return RET_OK
        for prop in self.grt:
            if prop in props and \
               self.check_item(group, prop, props[prop], getattr(groupinfo, self.grt[prop])) != RET_OK:
                r |= self.fix_item(group, prop, props[prop])
        return r

    def check(self):
        r = 0
        for group, props in self.groups.items():
            r |= self.check_group(group, props)
        return r

    def fix(self):
        r = 0
        for group, props in self.groups.items():
            r |= self.fix_group(group, props)
        return r

if __name__ == "__main__":
    main(CompGroup)
  0707010005ca2c000081fd0000000000000000000000015ab2175400001ed4000000660000000000000000000000000000004000000000root/var/lib/opensvc/compliance/com.opensvc/group_membership.py   #!/usr/bin/env python

data = {
  "default_prefix": "OSVC_COMP_GROUP_",
  "example_value": """
{
  "tibco": {
    "members": ["tibco", "tibco1"]
  },
  "tibco1": {
    "members": ["tibco1"]
  }
}
""",
  "description": """* Verify a local system group configuration
* A minus (-) prefix to the group name indicates the user should not exist

""",
  "form_definition": """
Desc: |
  A rule defining a list of Unix groups and their user membership. The referenced users and groups must exist.
Css: comp48

Outputs:
  -
    Dest: compliance variable
    Type: json
    Format: dict of dict
    Key: group
    EmbedKey: No
    Class: group_membership
Inputs:
  -
    Id: group
    Label: Group name
    DisplayModeLabel: group
    LabelCss: guys16
    Mandatory: Yes
    Type: string
    Help: The Unix group name.
  -
    Id: members
    Label: Group members
    DisplayModeLabel: members
    LabelCss: guy16
    Type: list of string
    Help: A comma-separed list of Unix user names members of this group.
""",
}

import os
import sys
import json
import grp
from subprocess import *
from utilities import which

sys.path.append(os.path.dirname(__file__))

from comp import *

class CompGroupMembership(CompObject):
    def __init__(self, prefix=None):
        CompObject.__init__(self, prefix=prefix, data=data)

    def init(self):
        self.member_of_h = {}
        self.grt = {
            'members': 'gr_mem',
        }
        self.sysname, self.nodename, x, x, self.machine = os.uname()

        if self.sysname not in ['SunOS', 'Linux', 'HP-UX', 'AIX', 'OSF1']:
            perror('group_membership: compliance object not supported on', self.sysname)
            raise NotApplicable

        self.groups = {}
        for d in self.get_rules():
            if type(d) != dict:
                continue
            for k, v in d.items():
                if "members" not in v:
                    continue
                for i, m in enumerate(v["members"]):
                    d[k]["members"][i] = m.strip()
            self.groups.update(d)

        if os.path.exists('/usr/xpg4/bin/id'):
            self.id_bin = '/usr/xpg4/bin/id'
        else:
            self.id_bin = 'id'

    def get_primary_group(self, user):
        cmd = [self.id_bin, "-gn", user]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            return
        return out.strip()

    def member_of(self, user, refresh=False):
        if not refresh and user in self.member_of_h:
            # cache hit
            return self.member_of_h[user]

        eg = self.get_primary_group(user)
        if eg is None:
            self.member_of_h[user] = []
            return []

        cmd = [self.id_bin, "-Gn", user]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            self.member_of_h[user] = []
            return self.member_of_h[user]
        ag = set(out.strip().split())
        ag -= set([eg])
        self.member_of_h[user] = ag
        return self.member_of_h[user]

    def fixable(self):
        return RET_NA

    def del_member(self, group, user):
        ag = self.member_of(user)
        if len(ag) == 0:
            return 0
        g = ag - set([group])
        g = ','.join(g)
        return self.fix_member(g, user)

    def add_member(self, group, user):
        if 0 != self._check_member_accnt(user):
            perror('group', group+':', 'cannot add inexistant user "%s"'%user)
            return RET_ERR
        if self.get_primary_group(user) == group:
            pinfo("group %s is already the primary group of user %s: skip declaration as a secondary group (you may want to change your rule)" % (group, user))
            return RET_OK
        ag = self.member_of(user)
        g = ag | set([group])
        g = ','.join(g)
        return self.fix_member(g, user)

    def fix_member(self, g, user):
        cmd = ['usermod', '-G', g, user]
        pinfo("group_membership:", ' '.join(cmd))
        p = Popen(cmd)
        out, err = p.communicate()
        r = p.returncode
        ag = self.member_of(user, refresh=True)
        if r == 0:
            return RET_OK
        else:
            return RET_ERR

    def fix_members(self, group, target):
        r = 0
        for user in target:
            if group in self.member_of(user):
                continue
            r += self.add_member(group, user)
        return r

    def fix_item(self, group, item, target):
        if item == 'members':
            return self.fix_members(group, target)
        else:
            perror("group_membership:", 'no fix implemented for', item)
            return RET_ERR

    def _check_member_accnt(self, user):
        if which('getent'):
            xcmd = ['getent', 'passwd', user]
        elif which('pwget'):
            xcmd = ['pwget', '-n', user]
        else:
            return 0
        xp = Popen(xcmd, stdout=PIPE, stderr=PIPE, close_fds=True)
        xout, xerr = xp.communicate()
        return xp.returncode

    def _check_members_accnts(self, group, user_list, which, verbose):
        r = RET_OK
        for user in user_list:
            rc = self._check_member_accnt(user)
            if rc != 0:
                r |= RET_ERR
                if verbose:
                    perror('group', group, '%s member "%s" does not exist'%(which, user))
        return r

    def filter_target(self, group, target):
        new_target = []
        for user in target:
            pg = self.get_primary_group(user)
            if pg == group:
                continue
            new_target.append(user)
        discarded = set(target)-set(new_target)
        if len(discarded) > 0:
            pinfo("group %s members discarded: %s, as they already use this group as primary (you may want to change your rule)" % (group, ', '.join(discarded)))
        return new_target
                
    def check_item(self, group, item, target, current, verbose=False):
        r = RET_OK
        if item == 'members':
            r |= self._check_members_accnts(group, current, 'existing', verbose)
            r |= self._check_members_accnts(group, target, 'target', verbose)
        if not isinstance(current, list):
            current = [current]
        target = self.filter_target(group, target)
        if set(target) <= set(current):
            if verbose:
                pinfo('group', group, item+':', ', '.join(current))
            return r
        else:
            if verbose:
                perror('group', group, item+':', ', '.join(current), '| target:', ', '.join(target))
            return r|RET_ERR

    def check_group(self, group, props):
        r = 0
        try:
            groupinfo = grp.getgrnam(group)
        except KeyError:
            pinfo('group', group, 'does not exist')
            return RET_OK
        for prop in self.grt:
            if prop in props:
                r |= self.check_item(group, prop, props[prop], getattr(groupinfo, self.grt[prop]), verbose=True)
        return r

    def fix_group(self, group, props):
        r = 0
        try:
            groupinfo = grp.getgrnam(group)
        except KeyError:
            pinfo('group', group, 'does not exist')
            return RET_OK
        for prop in self.grt:
            if prop in props and \
               self.check_item(group, prop, props[prop], getattr(groupinfo, self.grt[prop])) != RET_OK:
                r |= self.fix_item(group, prop, props[prop])
        return r

    def check(self):
        r = 0
        for group, props in self.groups.items():
            r |= self.check_group(group, props)
        return r

    def fix(self):
        r = 0
        for group, props in self.groups.items():
            r |= self.fix_group(group, props)
        return r

if __name__ == "__main__":
    main(CompGroupMembership)
0707010005ca2d000081fd0000000000000000000000015ab2175400002da6000000660000000000000000000000000000003600000000root/var/lib/opensvc/compliance/com.opensvc/keyval.py #!/usr/bin/env python

data = {
  "default_prefix": "OSVC_COMP_GROUP_",
  "example_kwargs": {
    "path": "/etc/ssh/sshd_config",
  },
  "example_value": """
{
  "path": "/etc/ssh/sshd_config",
  "keys": [
    {
      "key": "PermitRootLogin",
      "op": "=",
      "value": "yes"
    },
    {
      "key": "PermitRootLogin",
      "op": "reset",
      "value": ""
    }
  ]
}

""",
  "description": """* Setup and verify keys in "key value" formatted configuration file.
* Example files: sshd_config, ssh_config, ntp.conf, ...
""",
  "form_definition": """
Desc: |
  A rule to set a list of parameters in simple keyword/value configuration file format. Current values can be checked as set or unset, strictly equal, or superior/inferior to their target value. By default, this object appends keyword/values not found, potentially creating duplicates. The 'reset' operator can be used to avoid such duplicates.
Outputs:
  -
    Dest: compliance variable
    Type: json
    Format: list of dict
    Class: keyval
Inputs:
  -
    Id: key
    Label: Key
    DisplayModeTrim: 64
    DisplayModeLabel: key
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help:
  -
    Id: op
    Label: Comparison operator
    DisplayModeLabel: op
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Default: "="
    Candidates:
      - reset
      - unset
      - "="
      - ">"
      - ">="
      - "<"
      - "<="
      - "IN"
    Help: The comparison operator to use to check the parameter current value. The 'reset' operator can be used to avoid duplicate occurence of the same keyword (insert a key reset after the last key set to mark any additional key found in the original file to be removed). The IN operator verifies the current value is one of the target list member. On fix, if the check is in error, it sets the first target list member. A "IN" operator value must be a JSON formatted list.
  -
    Id: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string or integer
    Help: The configuration file parameter target value.
""",
}


import os
import sys
import json

sys.path.append(os.path.dirname(__file__))

from comp import *
from keyval_parser import Parser, ParserError

class KeyVal(CompObject):
    def __init__(self, prefix=None, path=None):
        CompObject.__init__(self, prefix=prefix, data=data)
        self.cf = path

    def init(self):
        self.nocf = False
        self.file_keys = {}

        if self.cf:
            self.file_keys[self.cf] = {
                "target_n_key": {},
                "keys": [],
            }

        for rule in self.get_rules():
            if self.cf and "key" in rule:
                self.file_keys[self.cf]["keys"] += [rule]
                continue
            if "path" not in rule:
                continue
            if "keys" not in rule or not isinstance(rule["keys"], list):
                continue
            path = rule["path"]
            if path not in self.file_keys:
                self.file_keys[path] = {
                    "target_n_key": {},
                    "keys": rule["keys"],
                }
            else:
                self.file_keys[path]["keys"] += rule["keys"]

        for path, data in self.file_keys.items():
            for i, key in enumerate(data["keys"]):
                if data["keys"][i]['op'] == 'IN':
                    data["keys"][i]['value'] = json.loads(data["keys"][i]['value'])
                if 'op' in key and 'key' in key and key['op'] not in ("unset", "reset"):
                    if key['key'] not in data["target_n_key"]:
                        data["target_n_key"][key['key']] = 1
                    else:
                        data["target_n_key"][key['key']] += 1
            try:
                data["conf"] = Parser(path)
            except ParserError as e:
                perror(e)
                raise ComplianceError()


    def fixable(self):
        return RET_OK

    def _check_key(self, path, data, keyname, target, op, value, instance=0, verbose=True):
        r = RET_OK
        if op == "reset":
            if value is not None:
                current_n_key = len(value)
                target_n_key = data["target_n_key"][keyname] if keyname in data["target_n_key"] else 0
                if current_n_key > target_n_key:
                    if verbose:
                        perror("%s is set %d times, should be set %d times"%(keyname, current_n_key, target_n_key))
                    return RET_ERR
                else:
                    if verbose:
                        pinfo("%s is set %d times, on target"%(keyname, current_n_key))
                    return RET_OK
            else:
                return RET_OK
        elif op == "unset":
            if value is not None:
                if is_string(target) and target.strip() == "":
                    if verbose:
                        perror("%s is set, should not be"%keyname)
                    return RET_ERR
                target_found = False
                for i, val in enumerate(value):
                    if target == val:
                        target_found = True
                        break

                if target_found:
                    if verbose:
                        perror("%s[%d] is set to value %s, should not be"%(keyname, i, target))
                    return RET_ERR
                else:
                    if verbose:
                        pinfo("%s is not set to value %s, on target"%(keyname, target))
                    return RET_OK
            else:
                if is_string(target) and target.strip() != "":
                    if verbose:
                        pinfo("%s=%s is not set, on target"%(keyname, target))
                else:
                    if verbose:
                        pinfo("%s is not set, on target"%keyname)
                return RET_OK

        if value is None:
            if op == 'IN' and "unset" in map(str, target):
                if verbose:
                    pinfo("%s is not set, on target"%(keyname))
                return RET_OK
            else:
                if verbose:
                    perror("%s[%d] is not set, target: %s"%(keyname, instance, str(target)))
                return RET_ERR

        if type(value) == list:
            if str(target) in value:
                if verbose:
                    pinfo("%s[%d]=%s on target"%(keyname, instance, str(value)))
                return RET_OK
            else:
                if verbose:
                    perror("%s[%d]=%s is not set"%(keyname, instance, str(target)))
                return RET_ERR

        if op == '=':
            if str(value) != str(target):
                if verbose:
                    perror("%s[%d]=%s, target: %s"%(keyname, instance, str(value), str(target)))
                r |= RET_ERR
            elif verbose:
                pinfo("%s=%s on target"%(keyname, str(value)))
        elif op == 'IN':
            if str(value) not in map(str, target):
                if verbose:
                    perror("%s[%d]=%s, target: %s"%(keyname, instance, str(value), str(target)))
                r |= RET_ERR
            elif verbose:
                pinfo("%s=%s on target"%(keyname, str(value)))
        else:
            if type(value) != int:
                if verbose:
                    perror("%s[%d]=%s value must be integer"%(keyname, instance, str(value)))
                r |= RET_ERR
            elif op == '<=' and value > target:
                if verbose:
                    perror("%s[%d]=%s target: <= %s"%(keyname, instance, str(value), str(target)))
                r |= RET_ERR
            elif op == '>=' and value < target:
                if verbose:
                    perror("%s[%d]=%s target: >= %s"%(keyname, instance, str(value), str(target)))
                r |= RET_ERR
            elif verbose:
                pinfo("%s[%d]=%s on target"%(keyname, instance, str(value)))
        return r

    def check_key(self, path, data, key, instance=0, verbose=True):
        if 'key' not in key:
            if verbose:
                perror("'key' not set in rule %s"%str(key))
            return RET_NA
        if 'value' not in key:
            if verbose:
                perror("'value' not set in rule %s"%str(key))
            return RET_NA
        if 'op' not in key:
            op = "="
        else:
            op = key['op']
        target = key['value']

        allowed_ops = ('>=', '<=', '=', 'unset', 'reset', 'IN')
        if op not in allowed_ops:
            if verbose:
                perror(key['key'], "'op' value must be one of", ", ".join(allowed_ops))
            return RET_NA

        keyname = key['key']
        value = data["conf"].get(keyname, instance=instance)

        r = self._check_key(path, data, keyname, target, op, value, instance=instance, verbose=verbose)

        return r

    def fix_key(self, path, data, key, instance=0):
        if key['op'] == "unset" or (key['op'] == "IN" and key['value'][0] == "unset"):
            pinfo("%s unset"%key['key'])
            if key['op'] == "IN":
                target = None
            else:
                target = key['value']
            data["conf"].unset(key['key'], target)
        elif key['op'] == "reset":
            target_n_key = data["target_n_key"][key['key']] if key['key'] in data["target_n_key"] else 0
            pinfo("%s truncated to %d definitions"%(key['key'], target_n_key))
            data["conf"].truncate(key['key'], target_n_key)
        else:
            if key['op'] == "IN":
                target = key['value'][0]
            else:
                target = key['value']
            pinfo("%s=%s set"%(key['key'], target))
            data["conf"].set(key['key'], target, instance=instance)

    def check(self):
        r = RET_OK
        for path, data in self.file_keys.items():
            r |= self.check_keys(path, data)
        return r

    def check_keys(self, path, data):
        r = RET_OK
        key_instance = {}
        for key in data["keys"]:
            if 'key' not in key or 'op' not in key:
                continue
            if key['op'] in ('reset', 'unset'):
                instance = None
            else:
                if key['key'] not in key_instance:
                    key_instance[key['key']] = 0
                else:
                    key_instance[key['key']] += 1
                instance = key_instance[key['key']]
            r |= self.check_key(path, data, key, instance=instance, verbose=True)
        return r

    def fix(self):
        r = RET_OK
        for path, data in self.file_keys.items():
            r |= self.fix_keys(path, data)
        return r

    def fix_keys(self, path, data):
        key_instance = {}
        for key in data["keys"]:
            if 'key' not in key or 'op' not in key:
                continue
            if key['op'] in ('reset', 'unset'):
                instance = None
            else:
                if key['key'] not in key_instance:
                    key_instance[key['key']] = 0
                else:
                    key_instance[key['key']] += 1
                instance = key_instance[key['key']]
            if self.check_key(path, data, key, instance=instance, verbose=False) == RET_ERR:
                self.fix_key(path, data, key, instance=instance)
        if not data["conf"].changed:
            return RET_OK
        try:
            data["conf"].write()
        except ParserError as e:
            perror(e)
            return RET_ERR
        return RET_OK

if __name__ == "__main__":
    main(KeyVal)
  0707010005ca30000081fd0000000000000000000000015ab2175400001812000000660000000000000000000000000000003d00000000root/var/lib/opensvc/compliance/com.opensvc/keyval_parser.py  #!/usr/bin/env python

import os
import sys
import datetime
import shutil

sys.path.append(os.path.dirname(__file__))

from comp import *

class ParserError(Exception):
    pass

class Parser(object):
    def __init__(self, path, section_markers=None):
        self.path = path
        self.data = {}
        self.changed = False
        self.nocf = False
        self.keys = []
        self.sections = {}
        self.section_names = []
        self.lastkey = '__lastkey__'
        self.comments = {self.lastkey: []}
        if section_markers:
            self.section_markers = section_markers
        else:
            self.section_markers = ["Match"]
        self.load()
        self.bkp = path + '.' + str(datetime.datetime.now())

    def __str__(self):
        s = ""
        for k in self.keys:
            if k in self.comments:
                s += '\n'.join(self.comments[k]) + '\n'
            s += '\n'.join([k + " " + str(v) for v in self.data[k]]) + '\n'
        if len(self.comments[self.lastkey]) > 0:
            s += '\n'.join(self.comments[self.lastkey])
        for section, data in self.sections.items():
            s += section + '\n'
            for k in data["keys"]:
                for v in data["data"][k]:
                    s += "\t" + k + " " + str(v) + '\n'
        return s

    def truncate(self, key, max):
        if key not in self.data:
            return
        n = len(self.data[key])
        if n <= max:
            return
        self.data[key] = self.data[key][:max]
        self.changed = True

    def set(self, key, value, instance=0):
        if key not in self.data:
            self.data[key] = [value]
            self.keys.append(key)
        elif instance >= len(self.data[key]):
            extra = instance + 1 - len(self.data[key])
            for i in range(len(self.data[key]), instance-1):
                self.data[key].append(None)
            self.data[key].append(value)
        else:
            self.data[key].insert(instance, value)
        self.changed = True

    def unset(self, key, value=None):
        if key in self.data:
            if value is not None and value.strip() != "":
                self.data[key].remove(value)
            else:
                self.data[key] = []
            if len(self.data[key]) == 0:
                del(self.data[key])
                if key in self.keys:
                    self.keys.remove(key)
        self.changed = True

    def get(self, key, instance=0):
        if key not in self.data:
            return
        if instance is None:
            return self.data[key]
        if instance < len(self.data[key]):
            return self.data[key][instance]
        return

    def load(self):
        if not os.path.exists(self.path):
            raise ParserError("%s does not exist"%self.path)
            self.nocf = True
            return
        with open(self.path, 'r') as f:
            buff = f.read()
        self.parse(buff)

    def backup(self):
        if self.nocf:
            return
        try:
            shutil.copy(self.path, self.bkp)
        except Exception as e:
            perror(e)
            raise ParserError("failed to backup %s"%self.path)
        pinfo("%s backup up as %s" % (self.path, self.bkp))

    def restore(self):
        if self.nocf:
            return
        try:
            shutil.copy(self.bkp, self.path)
        except:
            raise ParserError("failed to restore %s"%self.path)
        pinfo("%s restored from %s" % (self.path, self.bkp))


    def write(self):
        self.backup()
        try:
            with open(self.path, 'w') as f:
                f.write(str(self))
            pinfo("%s rewritten"%self.path)
        except Exception as e:
            perror(e)
            self.restore()
            raise ParserError()
        try:
            os.unlink(self.bkp)
        except OSError:
            pass

    def parse(self, buff):
        section = None

        for line in buff.splitlines():
            line = line.strip()

            # store comment line and continue
            if line.startswith('#') or len(line) == 0: 
                self.comments[self.lastkey].append(line)
                continue

            # strip end-of-line comment
            try:
                i = line.index('#')
                line = line[:i]
                line = line.strip()
            except ValueError:
                pass

            # discard empty line
            if len(line) == 0:
                continue

            l = line.split()
            if len(l) < 2:
                self.comments[self.lastkey].append(line)
                continue
            key = l[0]
            value = line[len(key):].strip()

            if key not in self.comments:
                self.comments[key] = self.comments[self.lastkey]
            else:
                self.comments[key] += self.comments[self.lastkey]
            self.comments[self.lastkey] = []

            try:
                value = int(value)
            except:
                pass

            if key in self.section_markers:
                section = key + " " + value
                if section not in self.sections:
                    self.sections[section] = {"keys": [], "data": {}}
                    self.section_names.append(section)
                continue

            if section:
                if key not in self.sections[section]["keys"]:
                    self.sections[section]["keys"].append(key)
                if key not in self.sections[section]["data"]:
                    self.sections[section]["data"][key] = []
                self.sections[section]["data"][key].append(value)
            else:
                if key not in self.keys:
                    self.keys.append(key)
                if key not in self.data:
                    self.data[key] = []
                self.data[key].append(value)

if __name__ == "__main__":
    if len(sys.argv) != 2:
        perror("wrong number of arguments")
        sys.exit(1)
    o = Parser(sys.argv[1])
    o.get("Subsystem")
    o.set("Subsystem", "foo")
    o.unset("PermitRootLogin")
    o.backup()
    pinfo(o)

  0707010005ca31000081fd0000000000000000000000015ab2175400003d3d000000660000000000000000000000000000003b00000000root/var/lib/opensvc/compliance/com.opensvc/linux.mpath.py    #!/usr/bin/env python

data = {
  "default_prefix": "OSVC_COMP_MPATH_",
  "example_value": """
[
  {
    "key": "defaults.polling_interval",
    "op": ">=",
    "value": 20
  },
  {
    "key": "device.{HP}.{HSV210.*}.prio",
    "op": "=",
    "value": "alua"
  },
  {
    "key": "blacklist.wwid",
    "value": 600600000001,
    "op": "="
  }
]
""",
  "description": """* Setup and verify the Linux native multipath configuration
""",
  "form_definition": """
Desc: |
  A rule to set a list of Linux multipath.conf parameters. Current values can be checked as strictly equal, or superior/inferior to their target value.
Outputs:
  -
    Dest: compliance variable
    Type: json
    Format: list of dict
    Class: linux_mpath
Inputs:
  -
    Id: key
    Label: Key
    DisplayModeTrim: 64
    DisplayModeLabel: key
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: >
     The multipath.conf parameter to check.
     ex: defaults.polling_interval or
         device.device.{HP}.{HSV210.*} or
         multipaths.multipath.6006000000000000 or
         blacklist.wwid or
         blacklist.device.{HP}.{HSV210.*}
  -
    Id: op
    Label: Comparison operator
    DisplayModeLabel: op
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Default: "="
    Candidates:
      - "="
      - ">"
      - ">="
      - "<"
      - "<="
    Help: The comparison operator to use to check the parameter current value.
  -
    Id: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string or integer
    Help: The multipath.conf parameter target value.
""",
}


import os
import sys
import json
import re
from subprocess import *

sys.path.append(os.path.dirname(__file__))

from comp import *

comment_chars = "#;"
sections_tree = {
  'defaults': {},
  'blacklist': {
    'device': {},
  },
  'blacklist_exceptions': {
    'device': {},
  },
  'devices': {
    'device': {},
  },
  'multipaths': {
    'multipath': {},
  },
}

class Blacklist(object):
    def __init__(self, name=""):
        self.name = name
        self.wwid = []
        self.devnode = []
        self.devices = []

    def __str__(self):
        s = ""
        if len(self.devices) + len(self.wwid) + len(self.devnode) == 0:
            return s
        s += self.name + " {\n"
        for wwid in self.wwid:
            s += "\twwid " + str(wwid) + "\n"
        for devnode in self.devnode:
            s += "\tdevnode " + str(devnode) + "\n"
        for device in self.devices:
            s += str(device)
        s += "}\n"
        return s

class Section(object):
    def __init__(self, name="", indent=1):
        self.name = name
        self.attr = {}
        self.indent = ""
        for i in range(indent):
            self.indent += '\t'

    def __str__(self):
        s = ""
        s += self.indent + self.name + " {\n"
        for a, v in self.attr.items():
            v = str(v)
            if ' ' in v:
                v = '"' + v + '"'
            s += self.indent + "\t" + a + " " + v + "\n"
        s += self.indent + "}\n"
        return s

class Conf(object):
    def __init__(self):
        self.blacklist = Blacklist("blacklist")
        self.blacklist_exceptions = Blacklist("blacklist_exceptions")
        self.defaults = Section("defaults", indent=0)
        self.devices = []
        self.multipaths = []
        self.changed = False

    def __str__(self):
        s = ""
        s += str(self.defaults)
        s += str(self.blacklist)
        s += str(self.blacklist_exceptions)
        if len(self.devices) > 0:
            s += "devices {\n"
            for device in self.devices:
                s += str(device)
            s += "}\n"
        if len(self.multipaths) > 0:
            s += "multipaths {\n"
            for multipath in self.multipaths:
                s += str(multipath)
            s += "}\n"
        return s

    def set(self, key, value):
        index = self.parse_key(key)
        key = re.sub(r'\{([^\}]+)\}\.', '', key)
        l = key.split('.')
        if key.endswith('}'):
            a = None
        else:
            a = l[-1]
        if l[1] == "device":
            o = self.find_device(l[0], index)
            if o is None:
                o = Section("device")
                o.attr['vendor'] = index[0]
                o.attr['product'] = index[1]
                _l = self.get_device_list(l[0])
                _l.append(o)
            if a is not None:
                o.attr[a] = value
            self.changed = True
        elif l[1] == "multipath":
            o = self.find_multipath(index)
            if o is None:
                o = Section("multipath")
                o.attr['wwid'] = index
                self.multipaths.append(o)
            o.attr[a] = value
            self.changed = True
        elif l[-1] == "wwid":
            o = getattr(self, l[0])
            o.wwid.append(str(value))
            self.changed = True
        elif l[-1] == "devnode":
            o = getattr(self, l[0])
            o.devnode.append(str(value))
            self.changed = True
        elif l[0] == "defaults":
            self.defaults.attr[a] = value
            self.changed = True

    def get(self, key):
        index = self.parse_key(key)
        key = re.sub(r'\{([^\}]+)\}\.', '', key)
        l = key.split('.')
        if key.endswith('}'):
            a = None
        else:
            a = l[-1]
        if len(l) < 2:
            perror("malformed key", key)
            return
        if l[1] == "device":
            o = self.find_device(l[0], index)
            if o:
                if a is None:
                    return ""
                elif a in o.attr:
                    return o.attr[a]
        elif l[1] == "multipath":
            o = self.find_multipath(index)
            if o and a in o.attr:
                return o.attr[a]
        elif l[-1] == "wwid":
            return getattr(self, l[0]).wwid
        elif l[-1] == "devnode":
            return getattr(self, l[0]).devnode
        elif l[0] == "defaults":
            if a in self.defaults.attr:
                return self.defaults.attr[a]

    def find_multipath(self, index):
        wwid = index
        for multipath in self.multipaths:
            if multipath.attr['wwid'] == wwid:
                return multipath

    def get_device_list(self, section):
        l = getattr(self, section)
        if type(l) != list and hasattr(l, "devices"):
            l = getattr(l, "devices")
        if type(l) != list:
            return
        return l
 
    def find_device(self, section, index):
        vendor, product = index
        l = self.get_device_list(section)
        if not l:
            return
        for device in l:
            if 'vendor' not in device.attr or \
               'product' not in device.attr:
                continue
            if device.attr['vendor'] == vendor and \
               device.attr['product'] == product:
                return device

    def parse_key(self, key):
        key = key.strip()
        m = re.search(r'device\.\{([^\}]+)\}\.\{([^\}]+)\}', key)
        if m:
            return m.group(1), m.group(2)

        m = re.search(r'multipath\.\{([^\}]+)\}', key)
        if m:
            return m.group(1)

class LinuxMpath(CompObject):
    def __init__(self, prefix=None):
        CompObject.__init__(self, prefix=prefix, data=data)

    def init(self):
        self.need_restart = False
        self.cf = os.path.join(os.sep, 'etc', 'multipath.conf')
        self.nocf = False
        self.conf = Conf()
        self.keys = self.get_rules()
        self.load_file(self.cf)

    def fixable(self):
        return RET_OK

    def load_file(self, p):
        if not os.path.exists(p):
            perror(p, "does not exist")
            self.nocf = True
            return
        with open(p, 'r') as f:
            buff = f.read()
        buff = self.strip_comments(buff)
        self._load_file(buff, sections_tree)

    def strip_comments(self, buff):
        lines = buff.split('\n')
        l = []
        for line in lines:
            line = line.strip()
            if len(line) == 0:
                continue
            discard = False
            for c in comment_chars:
                if line[0] == c:
                    discard = True
                    break
                try:
                    i = line.index(c)
                    line = line[:i]
                except ValueError:
                    pass
            if not discard and len(line) > 0:
                l.append(line)
        return "\n".join(l)

    def _load_file(self, buff, sections, chain=[]):
        for section, subsections in sections.items():
            _chain = chain + [section]
            _buff = buff
            while True:
                data = self.load_section(_buff, section)
                if data is None:
                    break
                _buff = data[1]
                self.load_keywords(data[0], subsections, _chain)
                self._load_file(data[0], subsections, _chain)

    def load_keywords(self, buff, subsections, chain):
        keywords = {}
        keyword = None
        for line in buff.split('\n'):
            if len(line) == 0:
                continue
            keyword = line.split()[0]
            if keyword in subsections:
                continue
            value = line[len(keyword):].strip().strip('"')
            if len(value) == 0:
                continue
            if keyword in ('wwid', 'devnode') and chain[-1].startswith('blacklist'):
                if keyword not in keywords:
                    keywords[keyword] = [value]
                else:
                    keywords[keyword] += [value]
            else:
                keywords[keyword] = value
        if chain[-1] == 'device' and chain[0] == 'devices':
            s = Section("device")
            s.attr = keywords
            self.conf.devices.append(s)
        elif chain[-1] == 'multipath':
            s = Section("multipath")
            s.attr = keywords
            self.conf.multipaths.append(s)
        elif chain[-1] == 'device' and chain[0] == 'blacklist':
            s = Section("device")
            s.attr = keywords
            self.conf.blacklist.devices.append(s)
        elif chain[-1] == 'device' and chain[0] == 'blacklist exceptions':
            s = Section("device")
            s.attr = keywords
            self.conf.blacklist_exceptions.devices.append(s)
        elif chain[-1] == 'blacklist':
            if 'wwid' in keywords:
                self.conf.blacklist.wwid = keywords['wwid']
            if 'devnode' in keywords:
                self.conf.blacklist.devnode = keywords['devnode']
        elif chain[-1] == 'blacklist_exceptions':
            if 'wwid' in keywords:
                self.conf.blacklist_exceptions.wwid = keywords['wwid']
            if 'devnode' in keywords:
                self.conf.blacklist_exceptions.devnode = keywords['devnode']
        elif chain[-1] == 'defaults':
            self.conf.defaults.attr = keywords

    def load_section(self, buff, section):
        l = []
        try:
            start = buff.index(section)
        except ValueError:
            return
        buff = buff[start:]
        try:
            buff = buff[buff.index('{')+1:]
        except ValueError:
            return
        depth = 1
        for i, c in enumerate(buff):
            if c == '{':
                depth += 1
            elif c == '}':
                depth -= 1
            if depth == 0:
                return buff[:i], buff[i+1:]
        return
                
    def _check_key(self, keyname, target, op, value, verbose=True):
        r = RET_OK
        if value is None:
            if verbose:
                perror("%s is not set"%keyname)
            return RET_ERR

        if type(value) == list:
            if str(target) in value:
                if verbose:
                    pinfo("%s=%s on target"%(keyname, str(value)))
                return RET_OK
            else:
                if verbose:
                    perror("%s=%s is not set"%(keyname, str(target)))
                return RET_ERR

        if op == '=':
            target = str(target).strip()
            if str(value) != target:
                if verbose:
                    perror("%s=%s, target: %s"%(keyname, str(value), target))
                r |= RET_ERR
            elif verbose:
                pinfo("%s=%s on target"%(keyname, str(value)))
        else:
            if type(value) != int:
                if verbose:
                    perror("%s=%s value must be integer"%(keyname, str(value)))
                r |= RET_ERR
            elif op == '<=' and value > target:
                if verbose:
                    perror("%s=%s target: <= %s"%(keyname, str(value), str(target)))
                r |= RET_ERR
            elif op == '>=' and value < target:
                if verbose:
                    perror("%s=%s target: >= %s"%(keyname, str(value), str(target)))
                r |= RET_ERR
            elif verbose:
                pinfo("%s=%s on target"%(keyname, str(value)))
        return r

    def check_key(self, key, verbose=True):
        if 'key' not in key:
            if verbose:
                perror("'key' not set in rule %s"%str(key))
            return RET_NA
        if 'value' not in key:
            if verbose:
                perror("'value' not set in rule %s"%str(key))
            return RET_NA
        if 'op' not in key:
            op = "="
        else:
            op = key['op']
        target = key['value']

        if op not in ('>=', '<=', '='):
            if verbose:
                perror("'op' must be either '=', '>=' or '<=': %s"%str(key))
            return RET_NA

        keyname = key['key']
        value = self.conf.get(keyname)

        if value is None:
            if verbose:
                perror("%s key is not set"%keyname)
            return RET_ERR

        r = self._check_key(keyname, target, op, value, verbose=verbose)

        return r

    def fix_key(self, key):
        pinfo("%s=%s set"%(key['key'], key['value']))
        self.conf.set(key['key'], key['value'])

    def check(self):
        r = 0
        for key in self.keys:
            r |= self.check_key(key, verbose=True)
        return r

    def fix(self):
        for key in self.keys:
            if self.check_key(key, verbose=False) == RET_ERR:
                self.fix_key(key)

        if not self.conf.changed:
            return

        if not self.nocf:
            self.backup(self.cf)
        try:
            with open(self.cf, 'w') as f:
                f.write(str(self.conf))
            pinfo(self.cf, "rewritten")
            self.need_restart = True
        except:
            perror("failed to write %s"%self.cf)
            return RET_ERR

        self.restart_daemon()

        return RET_OK

    def restart_daemon(self):
        if not self.need_restart:
            return
        candidates = [
          "/etc/init.d/multipathd",
          "/etc/init.d/multipath-tools",
        ]
        fpath = None
        for i in candidates:
            if os.path.exists(i):
                fpath = i
                break
        if fpath is None:
            perror("multipath tools startup script not found")
            return RET_ERR
        pinfo("restarting multipath daemon")
        cmd = [fpath, "restart"]
        p = Popen(cmd, stdin=None, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        err = bdecode(err)
        if len(err) > 0:
            perror(err)

if __name__ == "__main__":
    main(LinuxMpath)
   0707010005ca32000081fd0000000000000000000000015ab2175400001729000000660000000000000000000000000000003800000000root/var/lib/opensvc/compliance/com.opensvc/nodeconf.py   #!/usr/bin/env python
# -*- coding: utf-8 -*-

data = {
  "default_prefix": "OSVC_COMP_NODECONF_",
  "example_value": """
[
  {
    "key": "node.repopkg",
    "op": "=",
    "value": "ftp://ftp.opensvc.com/opensvc"
  },
  {
    "key": "node.repocomp",
    "op": "=",
    "value": "ftp://ftp.opensvc.com/compliance"
  }
]
""",
  "description": """* Verify opensvc agent configuration parameter
""",
  "form_definition": """
Desc: |
  A rule to set a parameter in OpenSVC node.conf configuration file. Used by the 'nodeconf' compliance object.
Css: comp48
Outputs:
  -
    Dest: compliance variable
    Type: json
    Format: list of dict
    Class: nodeconf
Inputs:
  -
    Id: key
    Label: Key
    DisplayModeLabel: key
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The OpenSVC node.conf parameter to check.
  -
    Id: op
    Label: Comparison operator
    DisplayModeLabel: op
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Default: "="
    Candidates:
      - "="
      - ">"
      - ">="
      - "<"
      - "<="
    Help: The comparison operator to use to check the parameter value.
  -
    Id: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string or integer
    Help: The OpenSVC node.conf parameter value to check.
""",
}


import os
import sys
import json
import re
from subprocess import *

sys.path.append(os.path.dirname(__file__))

from comp import *

class NodeConf(CompObject):
    def __init__(self, prefix=None):
        CompObject.__init__(self, prefix=prefix, data=data)

    def init(self):
        self.keys = self.get_rules()

    def fixable(self):
        return RET_OK

    def unset_val(self, keyname):
        cmd = ['nodemgr', 'unset', '--param', keyname]
        pinfo(' '.join(cmd))
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        return p.returncode

    def set_val(self, keyname, target):
        if type(target) == int:
            target = str(target)
        cmd = ['nodemgr', 'set', '--param', keyname, '--value', target]
        pinfo(' '.join(cmd))
        try:
            cmd[-1] = cmd[-1].encode("utf-8")
        except UnicodeDecodeError:
            pass
        env = os.environ
        env["LC_ALL"] = "C.UTF-8"
        p = Popen(cmd, stdout=PIPE, stderr=PIPE, env=env)
        out, err = p.communicate()
        return p.returncode

    def get_val(self, keyname):
        cmd = ['nodemgr', 'get', '--param', keyname]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            #perror('\n'.join((' '.join(cmd), out, err)))
            return
        if "deprecated" in bdecode(err):
            return
        out = bdecode(out).strip()
        try:
            out = int(out)
        except:
            pass
        return out

    def _check_key(self, keyname, target, op, value, verbose=True):
        r = RET_OK
        if value is None:
            if verbose:
                perror("%s not set"%keyname)
            r |= RET_ERR
        if op == '=':
            if str(value) != str(target):
                if verbose:
                    perror("%s=%s, target: %s"%(keyname, str(value), str(target)))
                r |= RET_ERR
            elif verbose:
                pinfo("%s=%s on target"%(keyname, str(value)))
        elif op == 'unset':
            if verbose:
                perror("%s=%s value must be unset"%(keyname, str(value)))
            r |= RET_ERR
        else:
            if type(value) != int:
                if verbose:
                    perror("%s=%s value must be integer"%(keyname, str(value)))
                r |= RET_ERR
            elif op == '<=' and value > target:
                if verbose:
                    perror("%s=%s target: <= %s"%(keyname, str(value), str(target)))
                r |= RET_ERR
            elif op == '>=' and value < target:
                if verbose:
                    perror("%s=%s target: >= %s"%(keyname, str(value), str(target)))
                r |= RET_ERR
            elif verbose:
                pinfo("%s=%s on target"%(keyname, str(value)))
        return r

    def check_key(self, key, verbose=True):
        if 'key' not in key:
            if verbose:
                perror("'key' not set in rule %s"%str(key))
            return RET_NA
        if 'value' not in key:
            if verbose:
                perror("'value' not set in rule %s"%str(key))
            return RET_NA
        if 'op' not in key:
            op = "="
        else:
            op = key['op']
        target = key['value']

        if op not in ('>=', '<=', '=', 'unset'):
            if verbose:
                perror("'value' list member 0 must be either '=', '>=', '<=' or unset: %s"%str(key))
            return RET_NA

        keyname = key['key']
        value = self.get_val(keyname)

        if value is None:
            if op == 'unset':
                 if verbose:
                     pinfo("%s key is not set"%keyname)
                 return RET_OK
            else:
                 if verbose:
                     perror("%s key is not set"%keyname)
                 return RET_ERR

        return self._check_key(keyname, target, op, value, verbose)

    def fix_key(self, key):
        if 'op' not in key:
            op = "="
        else:
            op = key['op']
        if op == "unset":
            return self.unset_val(key['key'])
        else:
            return self.set_val(key['key'], key['value'])

    def check(self):
        r = 0
        for key in self.keys:
            r |= self.check_key(key, verbose=True)
        return r

    def fix(self):
        r = 0
        for key in self.keys:
            if self.check_key(key, verbose=False) == RET_ERR:
                r += self.fix_key(key)
        return r

if __name__ == "__main__":
    main(NodeConf)

   0707010005ca33000081fd0000000000000000000000015ab2175400006696000000660000000000000000000000000000003700000000root/var/lib/opensvc/compliance/com.opensvc/package.py    #!/usr/bin/env python

data = {
  "default_prefix": "OSVC_COMP_PACKAGES_",
  "example_value": """ 
[
 "bzip2",
 "-zip",
 "zip"
]
  """,
  "description": """* Verify a list of packages is installed or removed
* A '-' prefix before the package name means the package should be removed
* No prefix before the package name means the package should be installed
* The package version is not checked
""",
  "form_definition": """
Desc: |
  A rule defining a set of packages, fed to the 'packages' compliance object for it to check each package installed or not-installed status.
Css: comp48

Outputs:
  -
    Dest: compliance variable
    Class: package
    Type: json
    Format: list

Inputs:
  -
    Id: pkgname
    Label: Package name
    DisplayModeLabel: ""
    LabelCss: pkg16
    Mandatory: Yes
    Help: Use '-' as a prefix to set 'not installed' as the target state. Use '*' as a wildcard for package name expansion for operating systems able to list packages available for installation.
    Type: string
""",
}

import os
import re
import sys
import json
import pwd
import tempfile
from subprocess import *
from utilities import which

sys.path.append(os.path.dirname(__file__))

from comp import *

class CompPackages(CompObject):
    def __init__(self, prefix='OSVC_COMP_PACKAGES_', uri=None):
        CompObject.__init__(self, prefix=prefix, data=data)
        self.uri = uri

    def init(self):
        self.combo_fix = False
        self.sysname, self.nodename, x, x, self.machine = os.uname()
        self.known_archs = ['i386', 'i586', 'i686', 'x86_64', 'noarch', '*']

        if self.sysname not in ['Linux', 'AIX', 'HP-UX', 'SunOS', 'FreeBSD']:
            perror(__file__, 'module not supported on', self.sysname)
            raise NotApplicable()

        if 'OSVC_COMP_PACKAGES_PKG_TYPE' in os.environ and \
           os.environ['OSVC_COMP_PACKAGES_PKG_TYPE'] == "bundle":
            self.pkg_type = 'bundle'
        else:
            self.pkg_type = 'product'

        self.packages = self.get_rules()

        if len(self.packages) == 0:
            raise NotApplicable()

        self.data = {}
        l = []
        for pkg in self.packages:
            if type(pkg) == dict:
                l.append(pkg['pkgname'])
                self.data[pkg['pkgname']] = pkg
        if len(l) > 0:
            self.packages = l

        vendor = os.environ.get('OSVC_COMP_NODES_OS_VENDOR', 'unknown')
        release = os.environ.get('OSVC_COMP_NODES_OS_RELEASE', 'unknown')
        if vendor in ['Debian', 'Ubuntu']:
            self.get_installed_packages = self.deb_get_installed_packages
            self.pkg_add = self.apt_fix_pkg
            self.pkg_del = self.apt_del_pkg
        elif vendor in ['CentOS', 'Redhat', 'Red Hat'] or \
             (vendor == 'Oracle' and self.sysname == 'Linux'):
            if which("yum") is None:
                perror("package manager not found (yum)")
                raise ComplianceError()
            self.combo_fix = True
            self.get_installed_packages = self.rpm_get_installed_packages
            self.pkg_add = self.yum_fix_pkg
            self.pkg_del = self.yum_del_pkg
        elif vendor == "SuSE":
            if which("zypper") is None:
                perror("package manager not found (zypper)")
                raise ComplianceError()
            self.get_installed_packages = self.rpm_get_installed_packages
            self.pkg_add = self.zyp_fix_pkg
            self.pkg_del = self.zyp_del_pkg
        elif vendor == "FreeBSD":
            if which("pkg") is None:
                perror("package manager not found (pkg)")
                raise ComplianceError()
            self.get_installed_packages = self.freebsd_pkg_get_installed_packages
            self.pkg_add = self.freebsd_pkg_fix_pkg
            self.pkg_del = self.freebsd_pkg_del_pkg
        elif vendor in ['IBM']:
            self.get_installed_packages = self.aix_get_installed_packages
            self.pkg_add = self.aix_fix_pkg
            self.pkg_del = self.aix_del_pkg
            if self.uri is None:
                perror("resource must be set")
                raise NotApplicable()
        elif vendor in ['HP']:
            self.get_installed_packages = self.hp_get_installed_packages
            self.pkg_add = self.hp_fix_pkg
            self.pkg_del = self.hp_del_pkg
        elif vendor in ['Oracle']:
            self.get_installed_packages = self.sol_get_installed_packages
            self.pkg_add = self.sol_fix_pkg
            self.pkg_del = self.sol_del_pkg
        else:
            perror(vendor, "not supported")
            raise NotApplicable()

        self.load_reloc()
        self.packages = map(lambda x: x.strip(), self.packages)
        self.expand_pkgnames()
        self.installed_packages = self.get_installed_packages()

    def load_reloc(self):
        self.reloc = {}
        for i, pkgname in enumerate(self.packages):
            l = pkgname.split(':')
            if len(l) != 2:
                continue
            self.packages[i] = l[0]
            self.reloc[l[0]] = l[1]

    def expand_pkgnames(self):
        """ Expand wildcards and implicit arch
        """
        l = []
        for pkgname in self.packages:
            if (pkgname.startswith('-') or pkgname.startswith('+')) and len(pkgname) > 1:
                prefix = pkgname[0]
                pkgname = pkgname[1:]
            else:
                prefix = ''
            l += map(lambda x: prefix+x, self.expand_pkgname(pkgname, prefix))
        self.packages = l

    def expand_pkgname(self, pkgname, prefix):
        vendor = os.environ.get('OSVC_COMP_NODES_OS_VENDOR', 'unknown')
        release = os.environ.get('OSVC_COMP_NODES_OS_RELEASE', 'unknown')
        if vendor in ['CentOS', 'Redhat', 'Red Hat'] or (vendor == 'Oracle' and release.startswith('VM ')):
            return self.yum_expand_pkgname(pkgname, prefix)
        elif vendor == 'SuSE':
            return self.zyp_expand_pkgname(pkgname, prefix)
        elif vendor in ['IBM']:
            return self.aix_expand_pkgname(pkgname, prefix)
        return [pkgname]

    def aix_expand_pkgname(self, pkgname, prefix=''):
        """
LGTOnw.clnt:LGTOnw.clnt.rte:8.1.1.6::I:C:::::N:NetWorker Client::::0::
LGTOnw.man:LGTOnw.man.rte:8.1.1.6::I:C:::::N:NetWorker Man Pages::::0::

or for rpm lpp_source:

zlib                                                               ALL  @@R:zlib _all_filesets
   @@R:zlib-1.2.7-2 1.2.7-2

        """
        if not hasattr(self, "nimcache"):
            cmd = ['nimclient', '-o', 'showres', '-a', 'resource=%s'%self.uri, '-a', 'installp_flags=L']
            p = Popen(cmd, stdout=PIPE, stderr=PIPE)
            out, err = p.communicate()
            err = bdecode(err)
            self.lpp_type = "installp"
            if "0042-175" in err:
                # not a native installp lpp_source
                cmd = ['nimclient', '-o', 'showres', '-a', 'resource=%s'%self.uri]
                p = Popen(cmd, stdout=PIPE, stderr=PIPE)
                out, err = p.communicate()
                self.lpp_type = "rpm"
            out = bdecode(out)
            self.nimcache = out.splitlines()

        l = []
        if self.lpp_type == "rpm":
            l = self.aix_expand_pkgname_rpm(pkgname, prefix=prefix)
        elif self.lpp_type == "native":
            l = self.aix_expand_pkgname_native(pkgname, prefix=prefix)

        if len(l) == 0:
            l = [pkgname]
        return l

    def aix_expand_pkgname_rpm(self, pkgname, prefix=''):
        import fnmatch
        l = []
        for line in self.nimcache:
            line = line.strip()
            if len(line) == 0:
                continue
            words = line.split()
            if line.startswith("@@") and len(words) > 1:
                _pkgvers = words[1]
                if fnmatch.fnmatch(_pkgname, pkgname) and _pkgname not in l:
                    l.append(_pkgname)
            else:
                _pkgname = words[0]
        return l

    def aix_expand_pkgname_native(self, pkgname, prefix=''):
        import fnmatch
        l = []
        for line in self.nimcache:
            words = line.split(':')
            if len(words) < 5:
                continue
            _pkgvers = words[2]
            _pkgname = words[1].replace('-'+_pkgvers, '')
            if fnmatch.fnmatch(_pkgname, pkgname) and _pkgname not in l:
                l.append(_pkgname)
        return l

    def zyp_expand_pkgname(self, pkgname, prefix=''):
        arch_specified = False
        for arch in self.known_archs:
            if pkgname.endswith(arch):
                arch_specified = True
        cmd = ['zypper', '--non-interactive', 'packages']
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            if prefix != '-':
                perror('can not expand (cmd error)', pkgname, err)
                return []
            else:
                return [pkgname]
        out = bdecode(out)
        lines = out.splitlines()
        if len(lines) < 2:
            if prefix != '-':
                perror('can not expand', pkgname)
                return []
            else:
                return [pkgname]
        for i, line in enumerate(lines):
            if "--+--" in line:
                break
        lines = lines[i+1:]
        l = []
        for line in lines:
            words = map(lambda x: x.strip(), line.split(" | "))
            if len(words) != 5:
                continue
            _status, _repo, _name, _version, _arch = words
            if arch_specified:
                if _name != pkgname or (arch != '*' and arch != _arch):
                    continue
            else:
                if _name != pkgname:
                    continue
            _pkgname = '.'.join((_name, _arch))
            if _pkgname in l:
                continue
            l.append(_pkgname)

        if arch_specified or len(l) == 1:
            return l

        if os.environ['OSVC_COMP_NODES_OS_ARCH'] in ('i386', 'i586', 'i686', 'ia32'):
            archs = ('i386', 'i586', 'i686', 'ia32', 'noarch')
        else:
            archs = (os.environ['OSVC_COMP_NODES_OS_ARCH'], 'noarch')

        ll = []
        for pkgname in l:
            if pkgname.split('.')[-1] in archs:
                # keep only packages matching the arch
                ll.append(pkgname)

        return ll

    def yum_expand_pkgname(self, pkgname, prefix=''):
        arch_specified = False
        for arch in self.known_archs:
            if pkgname.endswith(arch):
                arch_specified = True
        cmd = ['yum', 'list', pkgname]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            if prefix != '-':
                perror('can not expand (cmd error)', pkgname, err)
                return []
            else:
                return [pkgname]
        out = bdecode(out)
        lines = out.splitlines()
        if len(lines) < 2:
            if prefix != '-':
                perror('can not expand', pkgname)
                return []
            else:
                return [pkgname]
        lines = lines[1:]
        l = []
        for line in lines:
            words = line.split()
            if len(words) != 3:
                continue
            if words[0] in ("Installed", "Available", "Loaded", "Updating"):
                continue
            if words[0] in l:
                continue
            l.append((words[0], words[1]))

        ll = []
        ix86_added = False
        from distutils.version import LooseVersion as V
        for _pkgname, _version in sorted(l, key=lambda x: V(x[1]), reverse=True):
            pkgarch = _pkgname.split('.')[-1]
            if pkgarch not in ('i386', 'i586', 'i686', 'ia32'):
                #pinfo("add", _pkgname, "because", pkgarch, "not in ('i386', 'i586', 'i686', 'ia32')")
                ll.append(_pkgname)
            elif not ix86_added:
                #pinfo("add", _pkgname, "because", pkgarch, "not ix86_added")
                ll.append(_pkgname)
                ix86_added = True
        l = ll

        if arch_specified or len(l) == 1:
            return l

        if os.environ['OSVC_COMP_NODES_OS_ARCH'] in ('i386', 'i586', 'i686', 'ia32'):
            archs = ('i386', 'i586', 'i686', 'ia32', 'noarch')
        else:
            archs = (os.environ['OSVC_COMP_NODES_OS_ARCH'], 'noarch')

        ll = []
        for pkgname in l:
            pkgarch = pkgname.split('.')[-1]
            if pkgarch not in archs:
                # keep only packages matching the arch
                continue
            ll.append(pkgname)

        return ll

    def hp_parse_swlist(self, out):
        l = {}
        for line in out.split('\n'):
            if line.startswith('#') or len(line) == 0:
                continue
            v = line.split()
            if len(v) < 2:
                continue
            if v[0] in l:
                l[v[0]] += [(v[1], "")]
            else:
                l[v[0]] = [(v[1], "")]
        return l

    def hp_del_pkg(self, pkg):
        perror("TODO:", __fname__)
        return RET_ERR

    def hp_fix_pkg(self, pkg):
        if pkg in self.reloc:
            pkg = ':'.join((pkg, self.reloc[pkg]))
        cmd = ['swinstall',
               '-x', 'allow_downdate=true',
               '-x', 'mount_all_filesystems=false',
               '-s', self.uri, pkg]
        pinfo(" ".join(cmd))
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if len(out) > 0:
            pinfo(out)
        if len(err) > 0:
            perror(err)
        if p.returncode != 0:
            return RET_ERR
        return RET_OK

    def hp_get_installed_packages(self):
        p = Popen(['swlist', '-l', self.pkg_type], stdout=PIPE)
        (out, err) = p.communicate()
        if p.returncode != 0:
            perror('can not fetch installed packages list')
            return []
        out = bdecode(out)
        return self.hp_parse_swlist(out).keys()

    def get_free(self, c):
        if not os.path.exists(c):
            return 0
        cmd = ["df", "-k", c]
        p = Popen(cmd, stdout=PIPE, stderr=None)
        out, err = p.communicate()
        out = bdecode(out)
        for line in out.split():
            if "%" in line:
                l = out.split()
                for i, w in enumerate(l):
                    if '%' in w:
                        break
                try:
                    f = int(l[i-1])
                    return f
                except:
                    return 0
        return 0

    def get_temp_dir(self):
        if hasattr(self, "tmpd"):
            return self.tmpd
        candidates = ["/tmp", "/var/tmp", "/root"]
        free = {}
        for c in candidates:
            free[self.get_free(c)] = c
        max = sorted(free.keys())[-1]
        self.tmpd = free[max]
        pinfo("selected %s as temp dir (%d KB free)" % (self.tmpd, max))
        return self.tmpd

    def download(self, pkg_name):
        import urllib
        import tempfile
        f = tempfile.NamedTemporaryFile(dir=self.get_temp_dir())
        dname = f.name
        f.close()
        try:
            os.makedirs(dname)
        except:
            pass
        fname = os.path.join(dname, "file")
        try:
            self.urllib.urlretrieve(pkg_name, fname)
        except IOError:
            try:
                os.unlink(fname)
                os.unlink(dname)
            except:
                pass
            raise Exception("download failed: %s" % str(e))
        import tarfile
        os.chdir(dname)
        try:
            tar = tarfile.open(fname)
        except:
            pinfo("not a tarball")
            return fname
        try:
            tar.extractall()
        except:
            try:
                os.unlink(fname)
                os.unlink(dname)
            except:
                pass
            # must be a pkg
            return dname
        tar.close()
        os.unlink(fname)
        return dname

    def get_os_ver(self):
        cmd = ['uname', '-v']
        p = Popen(cmd, stdout=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            return 0
        out = bdecode(out)
        lines = out.splitlines()
        if len(lines) == 0:
            return 0
        try:
            osver = float(lines[0])
        except:
            osver = 0
        return osver

    def sol_get_installed_packages(self):
        p = Popen(['pkginfo', '-l'], stdout=PIPE)
        (out, err) = p.communicate()
        if p.returncode != 0:
            perror('can not fetch installed packages list')
            return []
        l = []
        out = bdecode(out)
        for line in out.splitlines():
            v = line.split(':')
            if len(v) != 2:
                continue
            f = v[0].strip()
            if f == "PKGINST":
                pkgname = v[1].strip()
                l.append(pkgname)
        return l

    def sol_del_pkg(self, pkg):
        if pkg not in self.installed_packages:
            return RET_OK
        yes = os.path.dirname(__file__) + "/yes"
        cmd = '%s | pkgrm %s' % (yes, pkg)
        pinfo(cmd)
        r = os.system(cmd)
        if r != 0:
            return RET_ERR
        return RET_OK

    def sol_fix_pkg(self, pkg):
        data = self.data[pkg]
        if 'repo' not in data or len(data['repo']) == 0:
            perror("no repo specified in the rule")
            return RET_NA

        if data['repo'].endswith("/"):
            pkg_url = data['repo']+"/"+pkg
        else:
            pkg_url = data['repo']
        pinfo("download", pkg_url)
        try:
            dname = self.download(pkg_url)
        except Exception as e:
            perror(e)
            return RET_ERR

        if os.path.isfile(dname):
            d = dname
        else:
            d = "."
            os.chdir(dname)

        if self.get_os_ver() < 10:
            opts = ''
        else:
            opts = '-G'
        if 'resp' in data and len(data['resp']) > 0:
            f = tempfile.NamedTemporaryFile(dir=self.get_temp_dir())
            resp = f.name
            f.close()
            with open(resp, "w") as f:
                f.write(data['resp'])
        else:
            resp = "/dev/null"
        yes = os.path.dirname(__file__) + "/yes"
        cmd = '%s | pkgadd -r %s %s -d %s all' % (yes, resp, opts, d)
        pinfo(cmd)
        r = os.system(cmd)

        os.chdir("/")
        if os.path.isdir(dname):
            import shutil
            shutil.rmtree(dname)
        if r != 0:
            return RET_ERR
        return RET_OK

    def aix_del_pkg(self, pkg):
        cmd = ['installp', '-u', pkg]
        pinfo(" ".join(cmd))
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if len(out) > 0:
            pinfo(out)
        if len(err) > 0:
            perror(err)
        if p.returncode != 0:
            return RET_ERR
        return RET_OK

    def aix_fix_pkg(self, pkg):
        cmd = ['nimclient', '-o', 'cust',
               '-a', 'lpp_source=%s'%self.uri,
               '-a', 'installp_flags=Y',
               '-a', 'filesets=%s'%pkg]
        s = " ".join(cmd)
        pinfo(s)
        r = os.system(s)
        if r != 0:
            return RET_ERR
        return RET_OK

    def aix_get_installed_packages(self):
        cmd = ['lslpp', '-Lc']
        p = Popen(cmd, stdout=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            perror('can not fetch installed packages list')
            return []
        pkgs = []
        out = bdecode(out)
        for line in out.splitlines():
            l = line.split(':')
            if len(l) < 5:
                continue
            pkgvers = l[2]
            pkgname = l[1].replace('-'+pkgvers, '')
            pkgs.append(pkgname)
        return pkgs

    def freebsd_pkg_get_installed_packages(self):
        p = Popen(['pkg', 'info'], stdout=PIPE)
        (out, err) = p.communicate()
        if p.returncode != 0:
            perror('can not fetch installed packages list')
            return []
        l = []
        out = bdecode(out)
        for line in out.splitlines():
            try:
                i = line.index(" ")
                line = line[:i]
                i = line.rindex("-")
                l.append(line[:i])
            except ValueError:
                pass
        return l

    def rpm_get_installed_packages(self):
        p = Popen(['rpm', '-qa', '--qf', '%{NAME}.%{ARCH}\n'], stdout=PIPE)
        (out, err) = p.communicate()
        if p.returncode != 0:
            perror('can not fetch installed packages list')
            return []
        out = bdecode(out)
        return out.splitlines()

    def deb_get_installed_packages(self):
        p = Popen(['dpkg', '-l'], stdout=PIPE)
        (out, err) = p.communicate()
        if p.returncode != 0:
            perror('can not fetch installed packages list')
            return []
        l = []
        out = bdecode(out)
        for line in out.splitlines():
            if not line.startswith('ii'):
                continue
            pkgname = line.split()[1]
            pkgname = pkgname.split(':')[0]
            l.append(pkgname)
        return l

    def freebsd_pkg_del_pkg(self, pkg):
        cmd = ['pkg', 'remove', '-y', pkg]
        pinfo(' '.join(cmd))
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            err = bdecode(err)
            if len(err) > 0:
                pinfo(err)
            return RET_ERR
        return RET_OK

    def freebsd_pkg_fix_pkg(self, pkg):
        cmd = ['pkg', 'install', '-y', pkg]
        pinfo(' '.join(cmd))
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            err = bdecode(err)
            if len(err) > 0:
                pinfo(err)
            return RET_ERR
        return RET_OK

    def zyp_del_pkg(self, pkg):
        cmd = ['zypper', 'remove', '-y', pkg]
        pinfo(' '.join(cmd))
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            err = bdecode(err)
            if len(err) > 0:
                pinfo(err)
            return RET_ERR
        return RET_OK

    def zyp_fix_pkg(self, pkg):
        cmd = ['zypper', 'install', '-y', pkg]
        pinfo(' '.join(cmd))
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            err = bdecode(err)
            if len(err) > 0:
                pinfo(err)
            return RET_ERR
        return RET_OK

    def yum_del_pkg(self, pkg):
        if type(pkg) == list:
            cmd = ['yum', '-y', 'remove'] + pkg
        else:
            cmd = ['yum', '-y', 'remove', pkg]
        pinfo(' '.join(cmd))
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            err = bdecode(err)
            if len(err) > 0:
                pinfo(err)
            return RET_ERR
        return RET_OK

    def yum_fix_pkg(self, pkg):
        cmd = ['yum', '-y', 'install'] + pkg
        pinfo(' '.join(cmd))
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            err = bdecode(err)
            if len(err) > 0:
                pinfo(err)
            return RET_ERR
        return RET_OK

    def apt_del_pkg(self, pkg):
        r = call(['apt-get', 'remove', '-y', pkg])
        if r != 0:
            return RET_ERR
        return RET_OK

    def apt_fix_pkg(self, pkg):
        r = call(['apt-get', 'install', '--allow-unauthenticated', '-y', pkg])
        if r != 0:
            return RET_ERR
        return RET_OK

    def fixable(self):
        return RET_NA

    def fix_pkg_combo(self):
        l_add = []
        l_del = []
        for pkg in self.packages:
            if pkg.startswith('-') and len(pkg) > 1:
                l_del.append(pkg[1:])
            elif pkg.startswith('+') and len(pkg) > 1:
                l_add.append(pkg[1:])
            else:
                l_add.append(pkg)
        if len(l_add) > 0:
            r = self.pkg_add(l_add)
            if r != RET_OK:
                return r
        if len(l_del) > 0:
            r = self.pkg_del(l_del)
            if r != RET_OK:
                return r
        return RET_OK
        
    def fix_pkg(self, pkg):
        if pkg.startswith('-') and len(pkg) > 1:
            return self.pkg_del(pkg[1:])
        if pkg.startswith('+') and len(pkg) > 1:
            return self.pkg_add(pkg[1:])
        else:
            return self.pkg_add(pkg)

    def check_pkg(self, pkg, verbose=True):
        if pkg.startswith('-') and len(pkg) > 1:
            return self.check_pkg_del(pkg[1:], verbose)
        if pkg.startswith('+') and len(pkg) > 1:
            return self.check_pkg_add(pkg[1:], verbose)
        else:
            return self.check_pkg_add(pkg, verbose)

    def check_pkg_del(self, pkg, verbose=True):
        if pkg in self.installed_packages:
            if verbose:
                perror('package', pkg, 'is installed')
            return RET_ERR
        if verbose:
            pinfo('package', pkg, 'is not installed')
        return RET_OK

    def check_pkg_add(self, pkg, verbose=True):
        if not pkg in self.installed_packages:
            if verbose:
                perror('package', pkg, 'is not installed')
            return RET_ERR
        if verbose:
            pinfo('package', pkg, 'is installed')
        return RET_OK

    def check(self):
        r = 0
        for pkg in self.packages:
            r |= self.check_pkg(pkg)
        return r

    def fix(self):
        r = 0
        if self.combo_fix:
            return self.fix_pkg_combo()
        for pkg in self.packages:
            if self.check_pkg(pkg, verbose=False) == RET_OK:
                continue
            r |= self.fix_pkg(pkg)
        return r

if __name__ == "__main__":
    main(CompPackages)
  0707010005ca34000081fd0000000000000000000000015ab2175400003404000000660000000000000000000000000000003700000000root/var/lib/opensvc/compliance/com.opensvc/process.py    #!/usr/bin/env python

data = {
  "default_prefix": "OSVC_COMP_PROC_",
  "example_value": """
[
  {
    "comm": "foo",
    "uid": 2345,
    "state": "on",
    "user": "foou"
  },
  {
    "comm": "bar",
    "state": "off",
    "uid": 2345
  }
]
""",
  "description": """* Checks if a process is present, specifying its comm, and optionnaly its owner's uid and/or username.
""",
  "form_definition": """
Desc: |
  A rule defining a process that should be running or not running on the target host, its owner's username and the command to launch it or to stop it.
Css: comp48
Outputs:
  -
    Dest: compliance variable
    Type: json
    Format: list of dict
    Class: process
Inputs:
  -
    Id: comm
    Label: Command
    DisplayModeLabel: comm
    LabelCss: action16
    Mandatory: No
    Type: string
    Help: The Unix process command, as shown in the ps comm column.
  -
    Id: args
    Label: Arguments
    DisplayModeLabel: args
    LabelCss: action16
    Mandatory: No
    Type: string
    Help: The Unix process arguments, as shown in the ps args column.
  -
    Id: state
    Label: State
    DisplayModeLabel: state
    LabelCss: action16
    Type: string
    Mandatory: Yes
    Default: on
    Candidates:
      - "on"
      - "off"
    Help: The expected process state.
  -
    Id: uid
    Label: Owner user id
    DisplayModeLabel: uid
    LabelCss: guy16
    Type: integer
    Help: The Unix user id owning the process.
  -
    Id: user
    Label: Owner user name
    DisplayModeLabel: user
    LabelCss: guy16
    Type: string
    Help: The Unix user name owning the process.
  -
    Id: start
    Label: Start command
    DisplayModeLabel: start
    LabelCss: action16
    Type: string
    Help: The command to start or stop the process, including the executable arguments. The executable must be defined with full path.
""",
}

import os
import sys
import json
import re
from subprocess import *

sys.path.append(os.path.dirname(__file__))

from comp import *
from utilities import which

class CompProcess(CompObject):
    def __init__(self, prefix=None):
        CompObject.__init__(self, prefix=prefix, data=data)

    def init(self):
        self.sysname, self.nodename, x, x, self.machine = os.uname()

        if self.sysname not in ['Linux', 'AIX', 'SunOS', 'FreeBSD', 'Darwin', 'HP-UX']:
            perror('module not supported on', self.sysname)
            raise NotApplicable()

        if self.sysname == 'HP-UX' and 'UNIX95' not in os.environ:
            os.environ['UNIX95'] = ""

        self.process = self.get_rules()
        self.validate_process()

        if len(self.process) == 0:
            raise NotApplicable()

        self.load_ps()

    def load_ps_args(self):
        self.ps_args = {}
        cmd = ['ps', '-e', '-o', 'pid,uid,user,args']
        p = Popen(cmd, stdout=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            perror("unable to fetch ps")
            raise ComplianceError
        out = bdecode(out)
        lines = out.splitlines()
        if len(lines) < 2:
            return
        for line in lines[1:]:
            l = line.split()
            if len(l) < 4:
                continue
            pid, uid, user = l[:3]
            args = " ".join(l[3:])
            if args not in self.ps_args:
                self.ps_args[args] = [(pid, int(uid), user)]
            else:
                self.ps_args[args].append((pid, int(uid), user))

    def load_ps_comm(self):
        self.ps_comm = {}
        cmd = ['ps', '-e', '-o', 'comm,pid,uid,user']
        p = Popen(cmd, stdout=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            perror("unable to fetch ps")
            raise ComplianceError
        out = bdecode(out)
        lines = out.splitlines()
        if len(lines) < 2:
            return
        for line in lines[1:]:
            l = line.split()
            if len(l) != 4:
                continue
            comm, pid, uid, user = l
            if comm not in self.ps_comm:
                self.ps_comm[comm] = [(pid, int(uid), user)]
            else:
                self.ps_comm[comm].append((pid, int(uid), user))

    def load_ps(self):
        self.load_ps_comm()
        self.load_ps_args()

    def validate_process(self):
        l = []
        for process in self.process:
            if self._validate_process(process) == RET_OK:
                l.append(process)
        self.process = l

    def _validate_process(self, process):
        if 'comm' not in process and 'args' not in process:
            perror(process, 'rule is malformed ... nor comm nor args key present')
            return RET_ERR
        if 'uid' in process and type(process['uid']) != int:
            perror(process, 'rule is malformed ... uid value must be integer')
            return RET_ERR
        return RET_OK

    def get_keys_args(self, args):
        found = []
        for key in self.ps_args:
            if re.match(args, key) is not None:
                found.append(key)
        return found

    def get_keys_comm(self, comm):
        found = []
        for key in self.ps_comm:
            if re.match(comm, key) is not None:
                found.append(key)
        return found

    def check_present_args(self, args, verbose):
        if len(args.strip()) == 0:
            return RET_OK
        found = self.get_keys_args(args)
        if len(found) == 0:
            if verbose:
                perror('process with args', args, 'is not started ... should be')
            return RET_ERR
        else:
            if verbose:
                pinfo('process with args', args, 'is started ... on target')
        return RET_OK

    def check_present_comm(self, comm, verbose):
        if len(comm.strip()) == 0:
            return RET_OK
        found = self.get_keys_comm(comm)
        if len(found) == 0:
            if verbose:
                perror('process with command', comm, 'is not started ... should be')
            return RET_ERR
        else:
            if verbose:
                pinfo('process with command', comm, 'is started ... on target')
        return RET_OK

    def check_present(self, process, verbose):
        r = RET_OK
        if 'comm' in process:
            r |= self.check_present_comm(process['comm'], verbose)
        if 'args' in process:
            r |= self.check_present_args(process['args'], verbose)
        return r

    def check_not_present_comm(self, comm, verbose):
        if len(comm.strip()) == 0:
            return RET_OK
        found = self.get_keys_comm(comm)
        if len(found) == 0:
           if verbose:
               pinfo('process with command', comm, 'is not started ... on target')
           return RET_OK
        else:
           if verbose:
               perror('process with command', comm, 'is started ... shoud be')
        return RET_ERR

    def check_not_present_args(self, args, verbose):
        if len(args.strip()) == 0:
            return RET_OK
        found = self.get_keys_args(args)
        if len(found) == 0:
           if verbose:
               pinfo('process with args', args, 'is not started ... on target')
           return RET_OK
        else:
           if verbose:
               perror('process with args', args, 'is started ... shoud be')
        return RET_ERR

    def check_not_present(self, process, verbose):
        r = 0
        if 'comm' in process:
            r |= self.check_not_present_comm(process['comm'], verbose)
        if 'args' in process:
            r |= self.check_not_present_args(process['args'], verbose)
        return r

    def check_process(self, process, verbose=True):
        r = RET_OK
        if process['state'] == 'on':
            r |= self.check_present(process, verbose)
            if r == RET_ERR:
                return RET_ERR
            if 'uid' in process:
                r |= self.check_uid(process, process['uid'], verbose)
            if 'user' in process:
                r |= self.check_user(process, process['user'], verbose)
        else:
            r |= self.check_not_present(process, verbose)

        return r

    def check_uid(self, process, uid, verbose):
        if 'args' in process:
            return self.check_uid_args(process['args'], uid, verbose)
        if 'comm' in process:
            return self.check_uid_comm(process['comm'], uid, verbose)

    def check_uid_comm(self, comm, uid, verbose):
        if len(comm.strip()) == 0:
            return RET_OK
        found = False
        keys = self.get_keys_comm(comm)
        for key in keys:
            for _pid, _uid, _user in self.ps_comm[key]:
                if uid == _uid:
                    found = True
                    continue
        if found:
            if verbose:
                pinfo('process with command', comm, 'runs with uid', _uid, '... on target')
        else:
            if verbose:
                perror('process with command', comm, 'does not run with uid', _uid, '... should be')
            return RET_ERR
        return RET_OK

    def check_uid_args(self, args, uid, verbose):
        if len(args.strip()) == 0:
            return RET_OK
        found = False
        keys = self.get_keys_args(args)
        for key in keys:
            for _pid, _uid, _user in self.ps_args[key]:
                if uid == _uid:
                    found = True
                    continue
        if found:
            if verbose:
                pinfo('process with args', args, 'runs with uid', _uid, '... on target')
        else:
            if verbose:
                perror('process with args', args, 'does not run with uid', _uid, '... should be')
            return RET_ERR
        return RET_OK

    def check_user(self, process, user, verbose):
        if 'args' in process:
            return self.check_user_args(process['args'], user, verbose)
        if 'comm' in process:
            return self.check_user_comm(process['comm'], user, verbose)

    def check_user_comm(self, comm, user, verbose):
        if len(comm.strip()) == 0:
            return RET_OK
        if user is None or len(user) == 0:
            return RET_OK
        found = False
        keys = self.get_keys_comm(comm)
        for key in keys:
            for _pid, _uid, _user in self.ps_comm[key]:
                if user == _user:
                    found = True
                    continue
        if found:
            if verbose:
                pinfo('process with command', comm, 'runs with user', _user, '... on target')
        else:
            if verbose:
                perror('process with command', comm, 'runs with user', _user, '... should run with user', user)
            return RET_ERR
        return RET_OK

    def check_user_args(self, args, user, verbose):
        if len(args.strip()) == 0:
            return RET_OK
        if user is None or len(user) == 0:
            return RET_OK
        found = False
        keys = self.get_keys_args(args)
        for key in keys:
            for _pid, _uid, _user in self.ps_args[key]:
                if user == _user:
                    found = True
                    continue
        if found:
            if verbose:
                pinfo('process with args', args, 'runs with user', _user, '... on target')
        else:
            if verbose:
                perror('process with args', args, 'runs with user', _user, '... should run with user', user)
            return RET_ERR
        return RET_OK

    def fix_process(self, process):
        if process['state'] == 'on':
            if self.check_present(process, verbose=False) == RET_OK:
                if ('uid' in process and self.check_uid(process, process['uid'], verbose=False) == RET_ERR) or \
                   ('user' in process and self.check_user(process, process['user'], verbose=False) == RET_ERR):
                    perror(process, "runs with the wrong user. can't fix.")
                    return RET_ERR
                return RET_OK
        elif process['state'] == 'off':
            if self.check_not_present(process, verbose=False) == RET_OK:
                return RET_OK

        if 'start' not in process or len(process['start'].strip()) == 0:
            perror("undefined fix method for process", process['comm'])
            return RET_ERR

        v = process['start'].split(' ')
        if not which(v[0]):
            perror("fix command", v[0], "is not present or not executable")
            return RET_ERR
        pinfo('exec:', process['start'])
        try:
            p = Popen(v, stdout=PIPE, stderr=PIPE)
            out, err = p.communicate()
        except Exception as e:
            perror(e)
            return RET_ERR
        out = bdecode(out)
        err = bdecode(err)
        if len(out) > 0:
            pinfo(out)
        if len(err) > 0:
            perror(err)
        if p.returncode != 0:
            perror("fix up command returned with error code", p.returncode)
            return RET_ERR
        return RET_OK

    def check(self):
        r = 0
        for process in self.process:
            r |= self.check_process(process)
        return r

    def fix(self):
        r = 0
        for process in self.process:
            r |= self.fix_process(process)
        return r

if __name__ == "__main__":
    main(CompProcess)
0707010005ca35000081fd0000000000000000000000015ab21754000011a1000000660000000000000000000000000000003200000000root/var/lib/opensvc/compliance/com.opensvc/rc.py #!/usr/bin/env python
""" 
[{"service": "foo", "level": "2345", "state": "on"},
 {"service": "foo", "level": "016", "state": "off"},
 {"service": "bar", "state": "on"},
 ...]
"""

import os
import sys
import json
import pwd
import re
from subprocess import *

sys.path.append(os.path.dirname(__file__))

from comp import *

class CompRc(object):
    def __init__(self, prefix='OSVC_COMP_RC_'):
        self.prefix = prefix.upper()
        self.sysname, self.nodename, x, x, self.machine = os.uname()

        self.services = []
        for k in [key for key in os.environ if key.startswith(self.prefix)]:
            try:
                l = json.loads(os.environ[k])
                for i, d in enumerate(l):
                    for key, val in d.items():
                        d[key] = self.subst(val)
                    l[i] = d
                self.services += l
            except ValueError:
                perror('failed to concatenate', os.environ[k], 'to service list')

        self.validate_svcs()

        if len(self.services) == 0:
            raise NotApplicable()

        if self.sysname not in ['Linux', 'HP-UX']:
            perror(__file__, 'module not supported on', self.sysname)
            raise NotApplicable()

        vendor = os.environ.get('OSVC_COMP_NODES_OS_VENDOR', 'unknown')
        release = os.environ.get('OSVC_COMP_NODES_OS_RELEASE', 'unknown')
        if vendor in ['CentOS', 'Redhat', 'Red Hat', 'SuSE'] or \
           (vendor == 'Oracle' and self.sysname == 'Linux'):

            import chkconfig
            self.o = chkconfig.Chkconfig()
        elif vendor in ['Ubuntu', 'Debian', 'HP']:
            import sysvinit
            self.o = sysvinit.SysVInit()
        else:
            perror(vendor, "not supported")
            raise NotApplicable()

    def subst(self, v):
        if type(v) == list:
            l = []
            for _v in v:
                l.append(self.subst(_v))
            return l
        if type(v) != str and type(v) != unicode:
            return v
        p = re.compile('%%ENV:\w+%%')
        for m in p.findall(v):
            s = m.strip("%").replace('ENV:', '')
            if s in os.environ:
                _v = os.environ[s]
            elif 'OSVC_COMP_'+s in os.environ:
                _v = os.environ['OSVC_COMP_'+s]
            else:
                perror(s, 'is not an env variable')
                raise NotApplicable()
            v = v.replace(m, _v)
        return v

    def validate_svcs(self):
        l = []
        for i, svc in enumerate(self.services):
            if self.validate_svc(svc) == RET_OK:
                l.append(svc)
        self.svcs = l

    def validate_svc(self, svc):
        if 'service' not in svc:
            perror(svc, ' rule is malformed ... service key not present')
            return RET_ERR
        if 'state' not in svc:
            perror(svc, ' rule is malformed ... state key not present')
            return RET_ERR
        return RET_OK

    def check_svc(self, svc, verbose=True):
        if 'seq' in svc:
            seq = svc['seq']
        else:
            seq = None
        return self.o.check_state(svc['service'], svc['level'], svc['state'], seq=seq, verbose=verbose)

    def fix_svc(self, svc, verbose=True):
        if 'seq' in svc:
            seq = svc['seq']
        else:
            seq = None
        if self.check_svc(svc, verbose=False) == RET_OK:
            return RET_OK
        return self.o.fix_state(svc['service'], svc['level'], svc['state'], seq=seq)

    def check(self):
        r = 0
        for svc in self.services:
            r |= self.check_svc(svc)
        return r

    def fix(self):
        r = 0
        for svc in self.services:
            r |= self.fix_svc(svc)
        return r

if __name__ == "__main__":
    syntax = """syntax:
      %s PREFIX check|fixable|fix"""%sys.argv[0]
    if len(sys.argv) != 3:
        perror("wrong number of arguments")
        perror(syntax)
        sys.exit(RET_ERR)
    try:
        o = CompRc(sys.argv[1])
        if sys.argv[2] == 'check':
            RET = o.check()
        elif sys.argv[2] == 'fix':
            RET = o.fix()
        elif sys.argv[2] == 'fixable':
            RET = o.fixable()
        else:
            perror("unsupported argument '%s'"%sys.argv[2])
            perror(syntax)
            RET = RET_ERR
    except NotApplicable:
        sys.exit(RET_NA)
    except:
        import traceback
        traceback.print_exc()
        sys.exit(RET_ERR)

    sys.exit(RET)

   0707010005ca36000081fd0000000000000000000000015ab217540000093f000000660000000000000000000000000000003c00000000root/var/lib/opensvc/compliance/com.opensvc/remove_files.py   #!/usr/bin/env python

data = {
  "default_prefix": "OSVC_COMP_REMOVE_FILES_",
  "example_value": """
[
  "/tmp/foo",
  "/bar/to/delete"
]
""",
  "description": """* Verify files and file trees are uninstalled
""",
  "form_definition": """
Desc: |
  A rule defining a set of files to remove, fed to the 'remove_files' compliance object.
Css: comp48

Outputs:
  -
    Dest: compliance variable
    Class: remove_files
    Type: json
    Format: list

Inputs:
  -
    Id: path
    Label: File path
    DisplayModeLabel: ""
    LabelCss: edit16
    Mandatory: Yes
    Help: You must set paths in fully qualified form.
    Type: string
""",
}

import os
import sys
import re
import json
from glob import glob
import shutil

sys.path.append(os.path.dirname(__file__))

from comp import *

blacklist = [
  "/",
  "/root"
]

class CompRemoveFiles(CompObject):
    def __init__(self, prefix=None):
        CompObject.__init__(self, prefix=prefix, data=data)

    def init(self):
        patterns = self.get_rules()

        patterns = sorted(list(set(patterns)))
        self.files = self.expand_patterns(patterns)

        if len(self.files) == 0:
            pinfo("no files matching patterns")
            raise NotApplicable

    def expand_patterns(self, patterns):
        l = []
        for pattern in patterns:
            l += glob(pattern)
        return l

    def fixable(self):
        return RET_NA

    def check_file(self, _file):
        if not os.path.exists(_file):
            pinfo(_file, "does not exist. on target.")
            return RET_OK
        perror(_file, "exists. shouldn't")
        return RET_ERR

    def fix_file(self, _file):
        if not os.path.exists(_file):
            return RET_OK
        try:
            if os.path.isdir(_file) and not os.path.islink(_file):
                shutil.rmtree(_file)
            else:
                os.unlink(_file)
            pinfo(_file, "deleted")
        except Exception as e:
            perror("failed to delete", _file, "(%s)"%str(e))
            return RET_ERR
        return RET_OK

    def check(self):
        r = 0
        for _file in self.files:
            r |= self.check_file(_file)
        return r

    def fix(self):
        r = 0
        for _file in self.files:
            r |= self.fix_file(_file)
        return r

if __name__ == "__main__":
    main(CompRemoveFiles)
 0707010005ca37000081fd0000000000000000000000015ab21754000014f9000000660000000000000000000000000000004000000000root/var/lib/opensvc/compliance/com.opensvc/self.signed.cert.py   #!/usr/bin/env python
data = {
  "default_prefix": "OSVC_COMP_CERT_",
  "example_value": """ 
{
    "CN": "%%ENV:SERVICES_SVCNAME%%",
    "crt": "/srv/%%ENV:SERVICES_SVCNAME%%/data/nginx/conf/ssl/server.crt",
    "key": "/srv/%%ENV:SERVICES_SVCNAME%%/data/nginx/conf/ssl/server.key",
    "bits": 2048,
    "C": "FR",
    "ST": "Ile de France",
    "L": "Paris",
    "O": "OpenSVC",
    "OU": "Lab",
    "emailAddress": "support@opensvc.com",
    "alt_names": [
        {
            "dns": ""
        }
    ]
}
""",
  "description": """* Check the existance of a key/crt pair
* Create the key/crt pair
""",
  "form_definition": """
Desc: |
  Describe a self-signed certificate
Css: comp48

Outputs:
  -
    Dest: compliance variable
    Type: json
    Format: dict
    Class: authkey

Inputs:
  -
    Id: CN
    Label: Common name
    DisplayModeLabel: cn
    LabelCss: loc
    Mandatory: Yes
    Type: string
  -
    Id: crt
    Label: Cert path
    DisplayModeLabel: crt
    LabelCss: key
    Mandatory: Yes
    Type: string
    Help: Where to install the generated certificate
  -
    Id: key
    Label: Key path
    DisplayModeLabel: key
    LabelCss: key
    Mandatory: Yes
    Type: string
    Help: Where to install the generated key
  -
    Id: bits
    Label: Bits
    DisplayModeLabel: bits
    LabelCss: key
    Mandatory: Yes
    Type: integer
    Default: 2048
    Help: Defines the key length in bits
  -
    Id: C
    Label: Country name
    DisplayModeLabel: country
    LabelCss: loc
    Mandatory: Yes
    Default: FR
    Type: string
  -
    Id: ST
    Label: State or Province
    DisplayModeLabel: state
    LabelCss: loc
    Mandatory: Yes
    Default: Ile de France
    Type: string
  -
    Id: L
    Label: Locality name
    DisplayModeLabel: locality
    LabelCss: loc
    Mandatory: Yes
    Default: Paris
    Type: string
  -
    Id: O
    Label: Organization name
    DisplayModeLabel: org
    LabelCss: loc
    Mandatory: Yes
    Default: OpenSVC
    Type: string
  -
    Id: OU
    Label: Organization unit
    DisplayModeLabel: org unit
    LabelCss: loc
    Mandatory: Yes
    Default: IT
    Type: string
  -
    Id: emailAddress
    Label: Email address
    DisplayModeLabel: email
    LabelCss: loc
    Mandatory: Yes
    Default: admin@opensvc.com
    Type: string
  -
    Id: alt_names
    Label: Alternate names
    DisplayModeLabel: alt names
    LabelCss: loc
    Type: form
    Form: self.signed.cert.alt_names
    Default: []


Subform:

Desc: |
  Subform for the self.signed.cert form.
Css: comp48

Outputs:
  -
    Type: json
    Format: list of dict

Inputs:
  -
    Id: dns
    Label: DNS
    DisplayModeLabel: dns
    LabelCss: loc
    Type: string
    Help: An alternate service name

    """
}

import os
import sys

sys.path.append(os.path.dirname(__file__))

from comp import *
from utilities import which
from subprocess import *

class CompSelfSignedCert(CompObject):
    def __init__(self, prefix='OSVC_COMP_CERT_'):
        CompObject.__init__(self, prefix=prefix, data=data)

    def init(self):
        self.rules = self.get_rules()
        if which("openssl") is None:
            raise NotApplicable("openssl command not found")

    def check(self):
        r = 0
        for rule in self.rules:
            r |= self.check_rule(rule)
        return r

    def fix(self):
        r = 0
        for rule in self.rules:
            r |= self.fix_rule(rule)
        return r

    def check_rule(self, rule):
        r = RET_OK
        if not os.path.exists(rule["key"]):
            perror("key %s does not exist" % rule["key"])
            r = RET_ERR
        else:
            pinfo("key %s exists" % rule["key"])
        if not os.path.exists(rule["crt"]):
            perror("crt %s does not exist" % rule["crt"])
            r = RET_ERR
        else:
            pinfo("crt %s exists" % rule["crt"])
        return r

    def fix_rule(self, rule):
        if os.path.exists(rule["key"]) and os.path.exists(rule["crt"]):
            return RET_OK
        for k in ("key", "crt"):
            d = os.path.dirname(rule[k])
            if not os.path.isdir(d):
                if os.path.exists(d):
                    perror("%s exists but is not a directory" % d)
                    return RET_ERR
                else:
                    pinfo("mkdir -p %s" %d)
                    os.makedirs(d)
        l = [""]
        for k in ["C", "ST", "L", "O", "OU", "CN", "emailAddress"]:
            l.append(k+"="+rule[k])
        if "alt_names" in rule and len(rule["alt_names"]) > 0:
            dns = []
            for i, d in enumerate(rule["alt_names"]):
                dns.append("DNS.%d=%s" % (i+1, d["DNS"]))
            l.append("subjectAltName="+",".join(dns))
        l.append("")
        cmd = ["openssl", "req", "-x509", "-nodes",
               "-newkey", "rsa:%d" % rule["bits"],
               "-keyout", rule["key"],
               "-out", rule["crt"],
               "-days", "XXX",
               "-subj", "%s" % "/".join(l)]
        pinfo(" ".join(cmd))
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            if len(out) > 0:
                pinfo(out)
            if len(err) > 0:
                perror(err) 
            return RET_ERR
        return RET_OK

if __name__ == "__main__":
    main(CompSelfSignedCert)

   0707010005ca38000081fd0000000000000000000000015ab21754000039a5000000660000000000000000000000000000003700000000root/var/lib/opensvc/compliance/com.opensvc/smfcfgs.py    #!/usr/bin/env python

data = {
  "default_prefix": "OSVC_COMP_SMF_CFGS_",
  "example_value": """ 
[
    {
        "fmri": "svc:/network/ntp",
        "prop": "config/slew_always",
        "type": "boolean",
        "value": "true",
        "inorder": 0,
        "create": 1,
        "reload": 0,
        "sleep": 0
    },
    {
        "fmri": "svc:/network/dns/client",
        "prop": "config/nameserver",
        "type": "net_address",
        "value": "172.30.65.165 172.30.65.164",
        "inorder": 0,
        "create": 1,
        "reload": 0,
        "sleep": 6
    },
    {
        "fmri": "svc:/network/dns/client",
        "prop": "config/search",
        "type": "astring",
        "value": "cpdev.local cpprod.root.local cpgrp.root.local",
        "inorder": 1,
        "create": 1,
        "reload": 0,
        "sleep": 9
    }
]
  """,
  "description": """Define a list of FMRI with properties to check / set on the target system. Properties can contain substitution variables. List values can be ordered or not. The fix action can be inhibited.""",
  "form_definition": """
Desc: |
  Define a list of FMRI with properties to check / set on the target system. Properties can contain substitution variables.
Css: action48
 
Outputs:
  -
    Dest: compliance variable
    Type: json
    Format: list of dict
    Class: smfcfgs
 
Inputs:
  -
    Id: fmri
    Label: FMRI
    DisplayModeLabel: fmri
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: "The name of the FMRI."
 
  -
    Id: prop
    Label: Prop
    DisplayModeLabel: prop
    LabelCss: comp16
    Type: string
    Help: "The FMRI property name."
 
  -
    Id: type
    Label: Type
    DisplayModeLabel: type
    LabelCss: hd16
    Type: string
    Help: "The property type."
 
  -
    Id: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: hd16
    Type: string
    Help: "The target value of the property."
 
  -
    Id: inorder
    Label: InOrder
    DisplayModeLabel: inorder
    LabelCss: right16
    Type: integer
    Default: 0
    Help: "If set to 1 and value is a list, report an error if the current list members are not in the same order than the target list members."
 
  -
    Id: create
    Label: Create
    DisplayModeLabel: create
    LabelCss: check16
    Type: integer
    Default: 0
    Help: "If set to 0, the fix action does not create the missing SMF configuration, the check action reports an error in any case."
 
  -
    Id: reload
    Label: Reload
    DisplayModeLabel: reload
    LabelCss: check16
    Type: integer
    Default: 1
    Help: "Reload if modified."
 
  -
    Id: sleep
    Label: Sleep
    DisplayModeLabel: sleep
    LabelCss: time16
    Type: integer
    Default: 0
    Help: "Sleep for <n> seconds after each 'svcadm refresh' command."
    
"""
}

import os
import sys
import json
import re

from subprocess import *

sys.path.append(os.path.dirname(__file__))

from comp import *

class AutoInst(dict):
    """autovivification feature."""
    def __getitem__(self, item):
        try:
            return dict.__getitem__(self, item)
        except KeyError:
            value = self[item] = type(self)()
            return value

class SmfCfgS(CompObject):
    def __init__(self, prefix=None):
        CompObject.__init__(self, prefix=prefix, data=data)

    def init(self):
        self.sysname, self.nodename, self.osn, self.solv, self.machine = os.uname()
        self.data = []
        self.smfs = AutoInst()

        if self.sysname != "SunOS":
            raise NotApplicable()

        self.osver = float(self.osn)
        if self.osver < 5.11:
            pinfo('Only used on Solaris 11 and behond')
            return

        for rule in self.get_rules():
            try:
                self.data += self.add_fmri(rule)
            except InitError:
                continue
            except ValueError:
                perror('smfcfgs: failed to parse variable', rule)

        if len(self.files) == 0:
            raise NotApplicable()

        for f in self.data:
            s,p,t,v = self.get_fmri(f['fmri'], f['prop'])
            if s is None:
                continue
            cre = False
            if p is None:
                if f['create'] == 0:
                    perror('FMRI:%s, PROP:%s is absent and create is False' %(s,f['prop']))
                    continue
                else:
                    p = f['prop']
                    cre = True
            if f['inorder'] == 0:
                ino = False
            else:
                ino = True
            if f['reload'] == 0:
                rel = False
            else:
                rel = True
            
            self.smfs[f['fmri']][p] = { 'val': f['value'], 'rval': v,
                                        'typ': f['type'] , 'rtyp': t,
                                        'ino': ino,
                                        'cre': cre,
                                        'rel': rel,
                                        'slp': f['sleep']
                                      }

    def subst(self, v):
        if type(v) == list:
            l = []
            for _v in v:
                l.append(self.subst(_v))
            return l
        if type(v) != str and type(v) != unicode:
            return v
	p = re.compile('%%ENV:\w+%%')
        for m in p.findall(v):
            s = m.strip("%").replace('ENV:', '')
            if s in os.environ:
                _v = os.environ[s]
            elif 'OSVC_COMP_'+s in os.environ:
                _v = os.environ['OSVC_COMP_'+s]
            else:
                perror(s, 'is not an env variable')
                raise NotApplicable()
            v = v.replace(m, _v)
        return v

    def add_fmri(self, v):
        if type(v) == str or type(v) == unicode:
            d = json.loads(v)
        else:
            d = v
        l = []

        # recurse if multiple FMRI are specified in a list of dict
        if type(d) == list:
            for _d in d:
                l += self.add_fmri(_d)
            return l

        if type(d) != dict:
            perror("not a dict:", d)
            return l

        if 'fmri' not in d:
            perror('FMRI should be in the dict:', d)
            RET = RET_ERR
            return l
        if 'prop' not in d:
            perror('prop should be in the dict:', d)
            RET = RET_ERR
            return l
        if 'value' not in d:
            perror('value should be in the dict:', d)
            RET = RET_ERR
            return l
        if 'create' in d:
            if d['create'] == 1:
                if not 'type' in d:
                    perror('create True[1] needs a type:', d)
                    RET = RET_ERR
                    return l
        for k in ('fmri', 'prop', 'value', 'inorder', 'type', 'create', 'sleep'):
            if k in d:
                d[k] = self.subst(d[k])
        return [d]
            
    def fixable(self):
        return RET_NA

    def get_fmri(self, s, p):
        cmd = ['/usr/sbin/svccfg','-s', s, 'listprop', p]
        po = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = po.communicate()
        out = bdecode(out)
        err = bdecode(err)
        if po.returncode != 0:
            if "doesn't match" in err:
                pinfo('%s is absent => IGNORED' %self.service)
                return None,None,None,None
            else:
                perror(' '.join(cmd))
                raise ComplianceError()
        if len(out) < 2:
                return s,None,None,None

        x = out.strip('\n').split()
        if x[0] != p:
            perror(' '.join([s, 'wanted:%s'%p, 'got:%s'%x[0]]))
            raise ComplianceError()
        return s,p,x[1],x[2:]

    def check_smf_prop_cre(self, s, p, verbose=True):
        r = RET_OK
        if self.smfs[s][p]['cre']:
            if verbose:
                perror('NOK: %s Prop %s shall be created' %(s,p))
            r |= RET_ERR
            if self.smfs[s][p]['typ'] == '' or self.smfs[s][p]['typ'] == None:
                if verbose:
                    perror('NOK: %s type must be specified to create %s' %(s,p))
        return r,self.smfs[s][p]['cre']

    def check_smf_prop_typ(self, s, p, verbose=True):
        r = RET_OK
        if self.smfs[s][p]['typ'] == '' or self.smfs[s][p]['typ'] == None:
            if verbose:
                pinfo('%s Prop %s type is not checked' %(s,p))
        elif self.smfs[s][p]['typ'] != self.smfs[s][p]['rtyp']:
            if verbose:
                perror('NOK: %s Prop %s type Do Not match, got:%s, expected:%s' %(s,p,self.smfs[s][p]['rtyp'],self.smfs[s][p]['typ']))
            r |= RET_ERR
        else:
            if verbose:
                pinfo('%s Prop %s type %s is OK' %(s,p,self.smfs[s][p]['typ']))
            if self.smfs[s][p]['typ'] == '' or self.smfs[s][p]['typ'] == None:
                if verbose:
                    perror('NOK: %s type must be specified to create %s' %(s,p))
        return r

    def check_smf_prop_val(self, s, p, verbose=True):
        r = RET_OK
        rvs = ' '.join(self.smfs[s][p]['rval'])
        if self.smfs[s][p]['ino']:
            if self.smfs[s][p]['val'] == rvs:
                if verbose:
                    pinfo('%s Prop %s values match in right order [%s]' %(s,p,rvs))
            else:
                if verbose:
                    perror('NOK: %s Prop %s values Do Not match, got:[%s], expected:[%s]' %(s,p,rvs,self.smfs[s][p]['val']))
                r |= RET_ERR
        else:
            vv = self.smfs[s][p]['val'].split()
            m = True
            for v in vv:
                if not v in self.smfs[s][p]['rval']:
                    if verbose and len(self.smfs[s][p]['rval']) > 1 :
                        perror('%s Prop %s notfound %s' %(s,p,v))
                    m = False
                else:
                    if verbose and len(self.smfs[s][p]['rval']) > 1 :
                        pinfo('%s Prop %s found %s' %(s,p,v))
            if m:
                if verbose:
                    pinfo('%s Prop %s values match [%s]' %(s,p,rvs))
            else:
                if verbose:
                    perror('NOK: %s Prop %s values Do Not match, got:[%s], expected:[%s]' %(s,p,rvs,self.smfs[s][p]['val']))
                r |= RET_ERR
        return r

    def check_smfs(self, verbose=True):
        r = RET_OK
        for s in self.smfs:
            for p in self.smfs[s]:
                """
                pinfo('FMRI: ', s, 'PROP: ', p, 'TYP: ', self.smfs[s][p]['typ'], 'RTYP: ', self.smfs[s][p]['rtyp'], type(self.smfs[s][p]['val']), type(self.smfs[s][p]['rval']))
                pinfo('	', 'VALS: ', self.smfs[s][p]['val'])
                pinfo('	', 'RVALS: ', self.smfs[s][p]['rval'])
                """
                rx,c = self.check_smf_prop_cre(s, p, verbose=verbose)
                r |= rx
                if not c:
                    r |= self.check_smf_prop_typ(s, p, verbose=verbose)
                r |= self.check_smf_prop_val(s, p, verbose=verbose)
        return r

    def fix_smfs(self, verbose=False):
        r = RET_OK
        cmds = []
        for s in self.smfs:
            for p in self.smfs[s]:
                added = False
                rx,c = self.check_smf_prop_cre(s, p, verbose=verbose)
                vx = self.smfs[s][p]['val'].split()
                if c:
                   if rx == 0 :
                       pinfo('%s try to add %s %s: = %s' %(s,p,self.smfs[s][p]['typ'],self.smfs[s][p]['val']))
                       if len(vx) > 1:
                           sxok = True
                           for v in vx:
                               if not (v.startswith('"') and v.endswith('"')):
                                    """
                                    sxok = False
                                    break
                                    """
                           if sxok:
                                cmds.append(['/usr/sbin/svccfg', '-s', s, 'setprop', p, '=', self.smfs[s][p]['typ']+':', '(%s)'%self.smfs[s][p]['val']])
                                added = True
                           else:
                                perror('NOK: %s prop %s values must be within double quotes [%s]' %(s,p,self.smfs[s][p]['val']))
                                r |= RET_ERR
                       else:
                           cmds.append(['/usr/sbin/svccfg', '-s', s, 'setprop', p, '=', self.smfs[s][p]['typ']+':', self.smfs[s][p]['val']])
                           added = True
                   else:
                       perror('NOK: %s cannot add prop %s without a valid type' %(s,p))
                       r |= RET_ERR 
                else:
                   ry = self.check_smf_prop_val(s, p, verbose=verbose)
                   if ry != 0:
                       pinfo('%s try to fix %s = %s' %(s,p,self.smfs[s][p]['val']))
                       if len(vx) > 1:
                           sxok = True
                           for v in vx:
                               if not (v.startswith('"') and v.endswith('"')):
                                    """
                                    sxok = False
                                    break
                                    """
                           if sxok:
                                cmds.append(['/usr/sbin/svccfg', '-s', s, 'setprop', p, '=', '(%s)'%self.smfs[s][p]['val']])
                                added = True
                           else:
                                perror('NOK: %s prop %s values must be within double quotes [%s]' %(s,p,self.smfs[s][p]['val']))
                                r |= RET_ERR
                       else:
                           cmds.append(['/usr/sbin/svccfg', '-s', s, 'setprop', p, '=', self.smfs[s][p]['val']])
                           added = True
                if added:
                   if self.smfs[s][p]['rel']:
                       cmds.append(['/usr/sbin/svcadm', 'refresh' ,s])
                       if self.smfs[s][p]['slp'] != 0:
                           cmds.append(['/usr/bin/sleep' , '%d'%self.smfs[s][p]['slp']])
        for cmd in cmds:
            pinfo('EXEC:', ' '.join(cmd))
            p = Popen(cmd, stdout=PIPE, stderr=PIPE)
            out, err = p.communicate()
            err = bdecode(err)
            if p.returncode != 0:
               perror('Code=%s %s' %(p.returncode, err))
               r |= RET_ERR
        return r

    def check(self):
        if self.osver < 5.11:
            return RET_NA
        r = self.check_smfs()
        return r

    def fix(self):
        if self.osver < 5.11:
            return RET_NA
        r = self.fix_smfs()
        return r

if __name__ == "__main__":
    main(SmfCfgS)

   0707010005ca39000081fd0000000000000000000000015ab2175400000948000000660000000000000000000000000000003700000000root/var/lib/opensvc/compliance/com.opensvc/sudoers.py    #!/usr/bin/env python
""" 
Same as files compliance object, but verifies the sudoers
declaration syntax using visudo in check mode.

The variable format is json-serialized:

{
  "path": "/some/path/to/file",
  "fmt": "root@corp.com		%%HOSTNAME%%@corp.com",
  "uid": 500,
  "gid": 500,
}

Wildcards:
%%ENV:VARNAME%%		Any environment variable value
%%HOSTNAME%%		Hostname
%%SHORT_HOSTNAME%%	Short hostname

"""

import os
import sys
from subprocess import *

sys.path.append(os.path.dirname(__file__))

from comp import *
from files import CompFiles

class CompSudoers(CompFiles):
    def check_file_syntax(self, f, verbose=False):
        cmd = ['visudo', '-c', '-f', '-']
        p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate(input=bencode(f['fmt']))
        if p.returncode != 0:
            if verbose:
                perror("target sudoers rules syntax error.")
            else:
                perror("target sudoers rules syntax error. abort installation.")
        return p.returncode

    def check(self):
        r = 0
        for f in self.files:
            r |= self.check_file_syntax(f, verbose=True)
            r |= self.check_file(f, verbose=True)
        return r

    def fix(self):
        r = 0
        for f in self.files:
            if self.check_file_syntax(f):
                r |= 1
                # refuse to install a corrupted sudoers file
                continue
            r |= self.fix_file_fmt(f)
            r |= self.fix_file_mode(f)
            r |= self.fix_file_owner(f)
        return r


if __name__ == "__main__":
    syntax = """syntax:
      %s PREFIX check|fixable|fix"""%sys.argv[0]
    if len(sys.argv) != 3:
        perror("wrong number of arguments")
        perror(syntax)
        sys.exit(RET_ERR)
    try:
        o = CompSudoers(sys.argv[1])
        if sys.argv[2] == 'check':
            RET = o.check()
        elif sys.argv[2] == 'fix':
            RET = o.fix()
        elif sys.argv[2] == 'fixable':
            RET = o.fixable()
        else:
            perror("unsupported argument '%s'"%sys.argv[2])
            perror(syntax)
            RET = RET_ERR
    except ComplianceError:
        sys.exit(RET_ERR)
    except NotApplicable:
        sys.exit(RET_NA)
    except:
        import traceback
        traceback.print_exc()
        sys.exit(RET_ERR)

    sys.exit(RET)

0707010005ca3b000081fd0000000000000000000000015ab217540000270d000000660000000000000000000000000000003700000000root/var/lib/opensvc/compliance/com.opensvc/svcconf.py    #!/usr/bin/env python

data = {
  "default_prefix": "OSVC_COMP_GROUP_",
  "example_env": {
    "OSVC_COMP_SERVICES_SVCNAME": "testsvc",
  },
  "example_value": """
[
  {
    "value": "fd5373b3d938",
    "key": "container#1.run_image",
    "op": "="
  },
  {
    "value": "/bin/sh",
    "key": "container#1.run_command",
    "op": "="
  },
  {
    "value": "/opt/%%ENV:SERVICES_SVCNAME%%",
    "key": "DEFAULT.docker_data_dir",
    "op": "="
  },
  {
    "value": "no",
    "key": "container(type=docker).disable",
    "op": "="
  },
  {
    "value": 123,
    "key": "container(type=docker&&run_command=/bin/sh).newvar",
    "op": "="
  }
]
""",
  "description": """* Setup and verify parameters in a opensvc service configuration.

""",
  "form_definition": """
Desc: |
  A rule to set a parameter in OpenSVC <service>.conf configuration file. Used by the 'svcconf' compliance object.
Css: comp48
Outputs:
  -
    Dest: compliance variable
    Type: json
    Format: list of dict
    Class: svcconf
Inputs:
  -
    Id: key
    Label: Key
    DisplayModeLabel: key
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The OpenSVC <service>.conf parameter to check.
  -
    Id: op
    Label: Comparison operator
    DisplayModeLabel: op
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Default: "="
    Candidates:
      - "="
      - ">"
      - ">="
      - "<"
      - "<="
    Help: The comparison operator to use to check the parameter value.
  -
    Id: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string or integer
    Help: The OpenSVC <service>.conf parameter value to check.

""",
}


import os
import sys
import json
import re
import copy
from subprocess import *

sys.path.append(os.path.dirname(__file__))

from comp import *

class SvcConf(CompObject):
    def __init__(self, prefix=None):
        CompObject.__init__(self, prefix=prefix, data=data)

    def init(self):
        self.keys = []

        if "OSVC_COMP_SERVICES_SVCNAME" not in os.environ:
            pinfo("SERVICES_SVCNAME is not set")
            raise NotApplicable()

        self.svcname = os.environ['OSVC_COMP_SERVICES_SVCNAME']

        self.keys = self.get_rules()

        try:
            self.get_config_file(refresh=True)
        except Exception as e:
            perror("unable to load service configuration:", str(e))
            raise ComplianceError()

        self.sanitize_keys()
        self.expand_keys()

    def get_config_file(self, refresh=False):
       if not refresh:
           return self.svc_config
       cmd = ['svcmgr', '-s', self.svcname, 'json_config']
       p = Popen(cmd, stdout=PIPE, stderr=PIPE)
       out, err = p.communicate()
       out = bdecode(out)
       self.svc_config = json.loads(out)
       return self.svc_config

    def fixable(self):
        return RET_NA

    def set_val(self, keyname, target):
        if type(target) == int:
            target = str(target)
        cmd = ['svcmgr', '-s', self.svcname, 'set', '--param', keyname, '--value', target]
        pinfo(' '.join(cmd))
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        return p.returncode

    def get_val(self, keyname):
        section, var = keyname.split('.')
        if section not in self.svc_config:
            return None
        return self.svc_config[section].get(var)

    def _check_key(self, keyname, target, op, value, verbose=True):
        r = RET_OK
        if value is None:
            if verbose:
                perror("%s not set"%keyname)
            r |= RET_ERR
        if op == '=':
            if str(value) != str(target):
                if verbose:
                    perror("%s=%s, target: %s"%(keyname, str(value), str(target)))
                r |= RET_ERR
            elif verbose:
                pinfo("%s=%s on target"%(keyname, str(value)))
        else:
            if type(value) != int:
                if verbose:
                    perror("%s=%s value must be integer"%(keyname, str(value)))
                r |= RET_ERR
            elif op == '<=' and value > target:
                if verbose:
                    perror("%s=%s target: <= %s"%(keyname, str(value), str(target)))
                r |= RET_ERR
            elif op == '>=' and value < target:
                if verbose:
                    perror("%s=%s target: >= %s"%(keyname, str(value), str(target)))
                r |= RET_ERR
            elif verbose:
                pinfo("%s=%s on target"%(keyname, str(value)))
        return r

    def check_filter(self, section, filter):
        op = None
        i = 0
        try:
            i = filter.index("&&")
            op = "and"
        except ValueError:
            pass
        try:
            i = filter.index("||")
            op = "or"
        except ValueError:
            pass

        if i == 0:
            _filter = filter
            _tail = ""
        else:
            _filter = filter[:i]
            _tail = filter[i:].lstrip("&&").lstrip("||")

        r = self._check_filter(section, _filter)
        #pinfo(" _check_filter('%s', '%s') => %s" % (section, _filter, str(r)))

        if op == "and":
            r &= self.check_filter(section, _tail)
        elif op == "or":
            r |= self.check_filter(section, _tail)

        return r

    def _check_filter(self, section, filter):
        if "~=" in filter:
            return self._check_filter_reg(section, filter)
        elif "=" in filter:
            return self._check_filter_eq(section, filter)
        perror("invalid filter syntax: %s" % filter)
        return False

    def _check_filter_eq(self, section, filter):
        l = filter.split("=")
        if len(l) != 2:
            perror("invalid filter syntax: %s" % filter)
            return False
        key, val = l
        cur_val = self.svc_config[section].get(key)
        if cur_val is None:
            return False
        if str(cur_val) == str(val):
            return True
        return False

    def _check_filter_reg(self, section, filter):
        l = filter.split("~=")
        if len(l) != 2:
            perror("invalid filter syntax: %s" % filter)
            return False
        key, val = l
        val = val.strip("/")
        cur_val = self.svc_config[section].get(key)
        if cur_val is None:
            return False
        reg = re.compile(val)
        if reg.match(cur_val):
            return True
        return False

    def resolve_sections(self, s, filter):
        """
        s is a ressource section name (fs, container, app, sync, ...)
        filter is a regexp like expression
           container(type=docker)
           fs(mnt~=/.*tools/)
           container(type=docker&&run_image~=/opensvc\/collector_web:build.*/)
           fs(mnt~=/.*tools/||mnt~=/.*moteurs/)
        """
        result = [];
        eligiblesections = [];
        for section in self.svc_config.keys():
            if section.startswith(s+'#') or section == s:
                eligiblesections.append(section)
        for section in eligiblesections:
            if self.check_filter(section, filter):
                #pinfo("   =>", section, "matches filter")
                result.append(section)
        result.sort()
        return result

    def sanitize_keys(self, verbose=True):
        r = RET_OK
        for key in self.keys:
            if 'key' not in key:
                if verbose:
                    perror("'key' not set in rule %s"%str(key))
                r |= RET_NA
            if 'value' not in key:
                if verbose:
                    perror("'value' not set in rule %s"%str(key))
                r |= RET_NA
            if 'op' not in key:
                op = "="
            else:
                op = key['op']

            if op not in ('>=', '<=', '='):
                if verbose:
                    perror("'value' list member 0 must be either '=', '>=' or '<=': %s"%str(key))
                r |= RET_NA

        if r is not RET_OK:
            sys.exit(r)

    def expand_keys(self):
        expanded_keys = []

        for key in self.keys:
            keyname = key['key']
            target = key['value']
            op = key['op']
            sectionlist = [];
            reg1 = re.compile(r'(.*)\((.*)\)\.(.*)')
            reg2 = re.compile(r'(.*)\.(.*)')
            m = reg1.search(keyname)
            if m:
                section = m.group(1)
                filter = m.group(2)
                var = m.group(3)
                sectionlist = self.resolve_sections(section, filter)
                for resolvedsection in sectionlist:
                    newdict = {
                     'key': '.'.join([resolvedsection, var]),
                     'op': op,
                     'value': target
                    }
                    expanded_keys.append(newdict)
                continue
            m = reg2.search(keyname)
            if m:
                section = m.group(1)
                var = m.group(2)
                expanded_keys.append(copy.copy(key))
                continue

            # drop key

        self.keys = expanded_keys

    def check_key(self, key, verbose=True):
        op = key['op']
        target = key['value']
        keyname = key['key']

        value = self.get_val(keyname)

        if value is None:
            if verbose:
                perror("%s key is not set"%keyname)
            return RET_ERR

        return self._check_key(keyname, target, op, value, verbose)

    def fix_key(self, key):
        return self.set_val(key['key'], key['value'])

    def check(self):
        r = 0
        for key in self.keys:
            r |= self.check_key(key, verbose=True)
        return r

    def fix(self):
        r = 0
        for key in self.keys:
            if self.check_key(key, verbose=False) == RET_ERR:
                r += self.fix_key(key)
        return r

if __name__ == "__main__":
    main(SvcConf)
   0707010005ca3c000081fd0000000000000000000000015ab2175400000ee9000000660000000000000000000000000000003700000000root/var/lib/opensvc/compliance/com.opensvc/symlink.py    #!/usr/bin/env python

data = {
  "default_prefix": "OSVC_COMP_FILE_",
  "example_value": """ 
{
  "symlink": "/tmp/foo",
  "target": "/tmp/bar"
}
""",
  "description": """* Verify symlink's existance.
* The collector provides the format with wildcards.
* The module replace the wildcards with contextual values.
* In the 'fix' the symlink is created (and intermediate dirs if required).
* There is no check or fix for target's existance.
* There is no check or fix for mode or ownership of either symlink or target.
""",
  "form_definition": """
Desc: |
  A symfile rule, fed to the 'symlink' compliance object to create a Unix symbolic link.
Css: comp48

Outputs:
  -
    Dest: compliance variable
    Class: symlink
    Type: json
    Format: dict

Inputs:
  -
    Id: symlink
    Label: Symlink path
    DisplayModeLabel: symlink
    LabelCss: hd16
    Mandatory: Yes
    Help: The full path of the symbolic link to check or create.
    Type: string

  -
    Id: target
    Label: Target path
    DisplayModeLabel: target
    LabelCss: hd16
    Mandatory: Yes
    Help: The full path of the target file pointed by the symlink.
    Type: string
"""
}

import os
import errno
import sys
import stat
import re
import pwd
import grp

sys.path.append(os.path.dirname(__file__))

from comp import *

class InitError(Exception):
    pass

class CompSymlink(CompObject):
    def __init__(self, prefix='OSVC_COMP_SYMLINK_'):
        CompObject.__init__(self, prefix=prefix, data=data)

    def init(self):
        self.sysname, self.nodename, x, x, self.machine = os.uname()
        self.symlinks = []

        for rule in self.get_rules():
            try:
                self.symlinks += self.add_symlink(rule)
            except InitError:
                continue
            except ValueError:
                perror('symlink: failed to parse variable', rule)

    def add_symlink(self, v):
        if 'symlink' not in v:
            perror('symlink should be in the dict:', d)
            RET = RET_ERR
            return []
        if 'target' not in v:
            perror('target should be in the dict:', d)
            RET = RET_ERR
            return []
        return [v]

    def fixable(self):
        return RET_NA

    def check_symlink_exists(self, f):
        if not os.path.islink(f['symlink']):
            return RET_ERR
        return RET_OK

    def check_symlink(self, f, verbose=False):
        if not os.path.islink(f['symlink']):
            perror("symlink", f['symlink'], "does not exist")
            return RET_ERR
        if os.readlink(f['symlink']) != f['target']:
            perror("symlink", f['symlink'], "does not point to", f['target'])
            return RET_ERR
        if verbose:
            pinfo("symlink", f['symlink'], "->", f['target'], "is ok")
        return RET_OK

    def fix_symlink_notexists(self, f):
        if self.check_symlink_exists(f) == RET_OK:
            return RET_OK
        d = os.path.dirname(f['symlink'])
        if not os.path.exists(d):
           try:
               os.makedirs(d)
           except OSError as e:
               if e.errno == 20:
                   perror("symlink: can not create dir", d, "to host the symlink", f['symlink'], ": a parent is not a directory")
                   return RET_ERR
               raise
        try:
           os.symlink(f['target'], f['symlink'])
        except:
            return RET_ERR
        pinfo("symlink", f['symlink'], "->", f['target'], "created")
        return RET_OK

    def check(self):
        r = 0
        for f in self.symlinks:
            r |= self.check_symlink(f, verbose=True)
        return r

    def fix(self):
        r = 0
        for f in self.symlinks:
            r |= self.fix_symlink_notexists(f)
        return r

if __name__ == "__main__":
    main(CompSymlink)

   0707010005ca3d000081fd0000000000000000000000015ab21754000020a2000000660000000000000000000000000000003600000000root/var/lib/opensvc/compliance/com.opensvc/sysctl.py #!/usr/bin/env python

data = {
  "default_prefix": "OSVC_COMP_SYSCTL_",
  "example_value": """ 
{
  "key": "vm.lowmem_reserve_ratio",
  "index": 1,
  "op": ">",
  "value": 256
}
  """,
  "description": """* Verify a linux kernel parameter value is on target
* Live parameter value (sysctl executable)
* Persistent parameter value (/etc/sysctl.conf)
""",
  "form_definition": """
Desc: |
  A rule to set a list of Linux kernel parameters to be set in /etc/sysctl.conf. Current values can be checked as strictly equal, or superior/inferior to their target value. Each field in a vectored value can be tuned independantly using the index key.
Css: comp48

Outputs:
  -
    Dest: compliance variable
    Type: json
    Format: list of dict
    Class: sysctl

Inputs:
  -
    Id: key
    Label: Key
    DisplayModeLabel: key
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The /etc/sysctl.conf parameter to check.

  -
    Id: index
    Label: Index
    DisplayModeLabel: idx
    LabelCss: action16
    Mandatory: Yes
    Default: 0
    Type: integer
    Help: The /etc/sysctl.conf parameter to check.

  -
    Id: op
    Label: Comparison operator
    DisplayModeLabel: op
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Default: "="
    Candidates:
      - "="
      - ">"
      - ">="
      - "<"
      - "<="
    Help: The comparison operator to use to check the parameter current value.

  -
    Id: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string or integer
    Help: The /etc/sysctl.conf parameter target value.
""",
}

import os
import sys
import json
import pwd
from subprocess import *

sys.path.append(os.path.dirname(__file__))

from comp import *

class Sysctl(CompObject):
    def __init__(self, prefix=None):
        CompObject.__init__(self, prefix=prefix, data=data)

    def init(self):
        if os.uname()[0] != "Linux":
            raise NotApplicable()
        self.need_reload = False
        self.cf = os.path.join(os.sep, "etc", "sysctl.conf")
        if not os.path.exists(self.cf):
            perror(self.cf, 'does not exist')
            raise NotApplicable()

        self.keys = []
        self.cache = None

        self.keys = self.get_rules()

        if len(self.keys) == 0:
            raise NotApplicable()

        self.convert_keys()


    def fixable(self):
        return RET_OK

    def parse_val(self, val):
        val = list(map(lambda x: x.strip(), val.strip().split()))
        for i, e in enumerate(val):
            try:
                val[i] = int(e)
            except:
                pass
        return val

    def get_keys(self):
        with open(self.cf, 'r') as f:
            buff = f.read()

        if self.cache is None:
            self.cache = {}

        for line in buff.splitlines():
            line = line.strip()
            if line.startswith('#'):
                continue
            l = line.split('=')
            if len(l) != 2:
                continue
            key = l[0].strip()
            val = self.parse_val(l[1])
            self.cache[key] = val

    def get_live_key(self, key):
        p = Popen(['sysctl', key], stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            return None
        l = bdecode(out).split('=')
        if len(l) != 2:
            return None
        val = self.parse_val(l[1])
        return val
 
    def get_key(self, key):
        if self.cache is None:
            self.get_keys()
        if key not in self.cache:
            return None
        return self.cache[key]

    def fix_key(self, key):
        done = False
        target = key['value']
        index = key['index']

        with open(self.cf, 'r') as f:
            buff = f.read()

        lines = buff.split('\n')
        for i, line in enumerate(lines):
            line = line.strip()
            if line.startswith('#'):
                continue
            l = line.split('=')
            if len(l) != 2:
                continue
            keyname = l[0].strip()
            if key['key'] != keyname:
                continue
            if done:
                pinfo("sysctl: remove redundant key %s"%keyname)
                del lines[i]
                continue
            val = self.parse_val(l[1])
            if target == val[index]:
                done = True
                continue
            pinfo("sysctl: set %s[%d] = %s"%(keyname, index, str(target)))
            val[index] = target
            lines[i] = "%s = %s"%(keyname, " ".join(map(str, val)))
            done = True

        if not done:
            # if key is not in sysctl.conf, get the value from kernel
            val = self.get_live_key(key['key'])
            if val is None:
                perror("key '%s' not found in live kernel parameters" % key['key'])
                return RET_ERR
            if target != val[index]:
                val[index] = target
            pinfo("sysctl: set %s = %s"%(key['key'], " ".join(map(str, val))))
            lines += ["%s = %s"%(key['key'], " ".join(map(str, val)))]

        try:
            with open(self.cf, 'w') as f:
                f.write('\n'.join(lines))
        except:
            perror("failed to write sysctl.conf")
            return RET_ERR

        return RET_OK

    def convert_keys(self):
        keys = []
        for key in self.keys:
            keyname = key['key']
            value = key['value']
            if type(value) == list:
                if len(value) > 0 and type(value[0]) != list:
                    value = [value]
                for i, v in enumerate(value):
                    keys.append({
                      "key": keyname,
                      "index": i,
                      "op": v[0],
                      "value": v[1],
                    })
            elif 'key' in key and 'index' in key and 'op' in key and 'value' in key:
               keys.append(key)

        self.keys = keys

    def check_key(self, key, verbose=False):
        r = RET_OK
        keyname = key['key']
        target = key['value']
        op = key['op']
        i = key['index']
        current_value = self.get_key(keyname)
        current_live_value = self.get_live_key(keyname)

        if current_value is None:
            if verbose:
                perror("key '%s' not found in sysctl.conf"%keyname)
            return RET_ERR

        if op == "=" and str(current_value[i]) != str(target):
            if verbose:
                perror("sysctl err: %s[%d] = %s, target: %s"%(keyname, i, str(current_value[i]), str(target)))
            r |= RET_ERR
        elif op == ">=" and type(target) == int and current_value[i] < target:
            if verbose:
                perror("sysctl err: %s[%d] = %s, target: >= %s"%(keyname, i, str(current_value[i]), str(target)))
            r |= RET_ERR
        elif op == "<=" and type(target) == int and current_value[i] > target:
            if verbose:
                perror("sysctl err: %s[%d] = %s, target: <= %s"%(keyname, i, str(current_value[i]), str(target)))
            r |= RET_ERR
        else:
            if verbose:
                pinfo("sysctl ok: %s[%d] = %s, on target"%(keyname, i, str(current_value[i])))

        if r == RET_OK and current_live_value is not None and current_value != current_live_value:
            if verbose:
                perror("sysctl err: %s on target in sysctl.conf but kernel value is different"%(keyname))
            self.need_reload = True
            r |= RET_ERR

        return r

    def check(self):
        r = 0
        for key in self.keys:
            r |= self.check_key(key, verbose=True)
        return r

    def reload_sysctl(self):
        cmd = ['sysctl', '-e', '-p']
        pinfo("sysctl:", " ".join(cmd))
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        p.communicate()
        if p.returncode != 0:
            perror("reload failed")
            return RET_ERR
        return RET_OK

    def fix(self):
        r = 0
        for key in self.keys:
            if self.check_key(key, verbose=False) == RET_ERR:
                self.need_reload = True
                r |= self.fix_key(key)
        if self.need_reload:
            r |= self.reload_sysctl()
        return r

if __name__ == "__main__":
    main(Sysctl)
  0707010005ca40000081fd0000000000000000000000015ab2175400001e89000000660000000000000000000000000000003800000000root/var/lib/opensvc/compliance/com.opensvc/sysvinit.py   #!/usr/bin/env python

from subprocess import *
import os
import sys
import glob
import re

sys.path.append(os.path.dirname(__file__))

from comp import *

class InitError(Exception):
    pass

class UnknownService(Exception):
    pass

class SetError(Exception):
    pass

class SeqError(Exception):
    pass

class DupError(Exception):
    pass

class SysVInit(object):
    def __init__(self):
        self.load()

    def __str__(self):
        s = ""
        for svc in self.services:
            s += "%-20s %s\n"%(svc, ' '.join(map(lambda x: '%-4s'%x,  str(self.services[svc]))))
        return s


    def get_svcname(self, s):
        _s = os.path.basename(s)
        _svcname = re.sub(r'^[SK][0-9]+', '', _s)
        _seq = re.sub(r'[KS](\d+).+', r'\1', _s)
        if _s[0] == 'S':
            _state = 'on'
        elif _s[0] == 'K':
            _state = 'off'
        else:
            raise InitError("unexepected service name: %s"%s)
        return _state, _seq, _svcname

    def load(self):
        self.services = {}
        self.levels = (0, 1, 2, 3, 4, 5, 6)
        default = "none"

        self.base_d = "/etc"
        self.init_d = self.base_d + "/init.d"
        if not os.path.exists(self.init_d):
            self.base_d = "/sbin"
            self.init_d = self.base_d + "/init.d"
        if not os.path.exists(self.init_d):
            raise InitError("init dir not found")

        for l in self.levels:
            for s in glob.glob("%s/rc%d.d/[SK]*"%(self.base_d, l)):
                state, seq, svc = self.get_svcname(s)
                if svc not in self.services:
                    self.services[svc] = {seq: [default, default, default, default, default, default, default]}
                if seq not in self.services[svc]:
                    self.services[svc][seq] = [default, default, default, default, default, default, default]
                self.services[svc][seq][l] = state

    def activate(self, service, levels, seq):
        for l in levels:
            self.activate_one(service, levels, seq)

    def activate_one(self, service, level, seq):
        if len(service) == 0:
            SetError("service is empty")

        start_l = "S%s%s"%(seq,service)
        svc_p = "../init.d/"+service

        os.chdir(self.base_d+"/rc%s.d"%level)

        g = glob.glob("[SK]*%s"%service)
        if len(g) > 0:
            cmd = ['rm', '-f'] + g
            pinfo(" ".join(cmd))
            p = Popen(cmd, stdout=PIPE)
            out, err = p.communicate()
            if p.returncode != 0:
                raise SetError()

        cmd = ['ln', '-sf', svc_p, start_l]
        pinfo(" ".join(cmd))
        p = Popen(cmd, stdout=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            raise SetError()

    def deactivate_one(self, service, level, seq):
        if len(service) == 0:
            SetError("service is empty")
        stop_l = "K%s%s"%(seq,service)
        svc_p = "../init.d/"+service

        os.chdir(self.base_d+"/rc%s.d"%level)

        g = glob.glob("[SK]*%s"%service)
        if len(g) > 0:
            cmd = ['rm', '-f'] + g
            pinfo(" ".join(cmd))
            p = Popen(cmd, stdout=PIPE)
            out, err = p.communicate()
            if p.returncode != 0:
                raise SetError()

        cmd = ['ln', '-sf', svc_p, stop_l]
        pinfo(" ".join(cmd))
        p = Popen(cmd, stdout=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            raise SetError()

    def delete_one(self, service, level):
        if len(service) == 0:
            SetError("service is empty")
        g = glob.glob(self.base_d+"/rc%s.d"%level+"/*"+service)
        if len(g) == 0:
            return
        cmd = ['rm', '-f'] + g
        pinfo(" ".join(cmd))
        p = Popen(cmd, stdout=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            raise SetError()

    def check_init(self, service):
        init_f = os.path.join(self.init_d, service)
        if os.path.exists(init_f):
            return True
        return False

    def set_state(self, service, level, state, seq):
        if service in self.services and seq in self.services[service]:
            curstates = self.services[service][seq]

            if state != "del" and len(curstates) == 1 and curstates[int(level)] == state or \
               state == "del" and len(curstates) == 1 and curstates[int(level)] == "none":
                return

        if state == "on":
            self.activate_one(service, level, seq)
        elif state == "off":
            self.deactivate_one(service, level, seq)
        elif state == "del":
            self.delete_one(service, level)
        else:
            raise SetError()

    def get_state(self, service, level, seq):
        if service not in self.services:
            raise UnknownService()

        # compute the number of different launcher for this service in the runlevel
        l = []
        for _seq in self.services[service]:
            if self.services[service][_seq][level] != "none":
                l.append(self.services[service][_seq][level])

        if seq is None:
            if len(l) == 0:
                return "none"
            raise SeqError()

        if len(l) > 1:
            raise DupError()

        try:
            curstates = self.services[service][seq]
            curstate = curstates[int(level)]
        except:
            curstate = "none"

        if len(l) == 1 and curstate == "none":
            raise SeqError()
        return curstate

    def check_state(self, service, levels, state, seq=None, verbose=False):
        r = 0
        if seq is not None and type(seq) == int:
            seq = "%02d"%seq

        if not self.check_init(service):
            if verbose:
                perror("service %s init script does not exist in %s"%(service, self.init_d))
            r |= 1

        if seq is None and state != "del":
            if verbose:
                perror("service %s sequence number must be set"%(service))
            return 1

        for level in levels:
            try:
                level = int(level)
            except:
                continue
            try:
                curstate = self.get_state(service, level, seq)
            except DupError:
                if verbose:
                    perror("service %s has multiple launchers at level %d"%(service, level))
                r |= 1
                continue
            except SeqError:
                if verbose:
                    perror("service %s sequence number error at level %d"%(service, level))
                r |= 1
                continue
            except UnknownService:
                curstate = "none"

            if (state != "del" and curstate != state) or \
               (state == "del" and curstate != "none"):
                if verbose:
                    perror("service", service, "at runlevel", level, "is in state", curstate, "! target state is", state)
                r |= 1
            else:
                if verbose:
                    pinfo("service", service, "at runlevel", level, "is in state", curstate)
        return r
            
    def fix_state(self, service, levels, state, seq=None):
        if seq is not None and type(seq) == int:
            seq = "%02d"%seq

        if seq is None and state != "del":
            perror("service %s sequence number must be set"%(service))
            return 1

        for level in levels:
            try:
                self.set_state(service, level, state, seq)
            except SetError:
                perror("failed to set", service, "runlevels")
                return 1
        return 0

if __name__ == "__main__":
    o = SysVInit()
    pinfo(o)
    pinfo('xfs@rc3 =', o.get_state('xfs', 3))

   0707010005ca41000081fd0000000000000000000000015ab2175400001663000000660000000000000000000000000000003b00000000root/var/lib/opensvc/compliance/com.opensvc/timedatectl.py    #!/usr/bin/env python

data = {
  "default_prefix": "OSVC_COMP_TIMEDATECTL_",
  "example_value": """
    {
      "timezone": "Europe/Paris",
      "ntpenabled": "no"
    }
  """,
  "description": """* Checks timedatectl settings
* Module need to be called with the exposed target settings as variable (timedatectl.py OSVC_COMP_TIMEDATECTL_1 check)
""",
  "form_definition": """
Desc: |
  A timedatectl rule, fed to the 'timedatectl' compliance object to setup rhel/centos7+ timezone/ntp.
Css: comp48

Outputs:
  -
    Dest: compliance variable
    Class: timedatectl
    Type: json
    Format: dict

Inputs:
  -
    Id: timezone
    Label: Timezone
    DisplayModeLabel: timezone
    LabelCss: action16
    Mandatory: No
    Help: 'The timezone name, as listed by "timedatectl list-timezones" command. Example: Europe/Paris'
    Type: string

  -
    Id: ntpenabled
    Label: NTP Enabled
    DisplayModeLabel: ntpenabled
    LabelCss: time16
    Mandatory: No
    Default: "yes"
    Candidates:
      - "yes"
      - "no"
    Help: "Specify yes or no, to request enabling or disabling the chronyd time service, driven through timedatectl command."
    Type: string
"""
}

import os
import sys
from subprocess import *

sys.path.append(os.path.dirname(__file__))

from comp import *
from utilities import *

class CompTimeDateCtl(CompObject):
    def __init__(self, prefix=None):
        CompObject.__init__(self, prefix=prefix, data=data)

    def init(self):
        self.sysname, self.nodename, x, x, self.machine = os.uname()

        self.inputs = self.get_rules()[0]

        if self.sysname not in ['Linux']:
            perror('module not supported on', self.sysname)
            raise NotApplicable()

        if which('timedatectl') is None:
            perror('timedatectl command not found', self.sysname)
            raise NotApplicable()

        self.tz = self.get_valid_tz()
        self.live = self.get_current_tdctl()

    def get_current_tdctl(self):
        """
        [root@rhel71 averon]# timedatectl
              Local time: mar. 2016-03-29 17:13:43 CEST
          Universal time: mar. 2016-03-29 15:13:43 UTC
                RTC time: mar. 2016-03-29 15:13:42
               Time zone: Europe/Paris (CEST, +0200)
             NTP enabled: yes
        NTP synchronized: yes
         RTC in local TZ: no
              DST active: yes
         Last DST change: DST began at
                          dim. 2016-03-27 01:59:59 CET
                          dim. 2016-03-27 03:00:00 CEST
         Next DST change: DST ends (the clock jumps one hour backwards) at
                          dim. 2016-10-30 02:59:59 CEST
                          dim. 2016-10-30 02:00:00 CET
        """

        current = {}
        try:
            cmd = ['timedatectl', 'status']
            p = Popen(cmd, stdout=PIPE)
            out, err = p.communicate()
            if p.returncode != 0:
                raise
            out = bdecode(out)
            for line in out.splitlines():
                if 'Time zone:' in line:
                    s = line.split(':')[-1].strip()
                    t = s.split(' ')[0]
                    current['timezone'] = t
                if 'NTP enabled:' in line:
                    current['ntpenabled'] = line.split(':')[-1].strip()
        except:
            perror('can not fetch timedatectl infos')
            return None
        return current

    def get_valid_tz(self):
        tz = []
        try:
            cmd = ['timedatectl', '--no-pager', 'list-timezones']
            p = Popen(cmd, stdout=PIPE)
            out, err = p.communicate()
            if p.returncode != 0:
                raise
            out = bdecode(out)
            for line in out.splitlines():
                curtz = line.strip()
                if curtz is not '':
                    tz.append(curtz)
        except:
            perror('can not build valid timezone list')
            return None
        return tz

    def fixable(self):
        return RET_NA

    def check(self):
        if self.live is None:
            return RET_NA
        r = RET_OK
        for input in self.inputs:
            r |= self._check(input)
        return r

    def _check(self, input):
        if self.inputs[input] == self.live[input]:
            pinfo("timedatectl %s is %s, on target" % (input, self.live[input] ))
            return RET_OK
        perror("timedatectl %s is %s, target %s" % (input, self.live[input], self.inputs[input]))
        return RET_ERR

    def set_tz(self, timezone):
        try:
            cmd = ['timedatectl', 'set-timezone', timezone]
            p = Popen(cmd, stdout=PIPE)
            out, err = p.communicate()
            if p.returncode != 0:
                raise
        except:
            perror('could not set timezone')
            return None
        return RET_OK

    def set_ntp(self, value):
        try:
            cmd = ['timedatectl', 'set-ntp', value]
            p = Popen(cmd, stdout=PIPE)
            out, err = p.communicate()
            if p.returncode != 0:
                raise
        except:
            perror('could not set ntp')
            return None
        return RET_OK

    def _fix(self, input):
        r = RET_OK
        if input in 'timezone':
            r |= self.set_tz(self.inputs[input])
            return r
        if input in 'ntpenabled':
            r |= self.set_ntp(self.inputs[input])
            return r
        return RET_NA

    def fix(self):
        r = RET_OK
        if self.check() == RET_ERR:
            for input in self.inputs:
                r |= self._fix(input)
        return r

    def test(self):
        print("Not Implemented")

if __name__ == "__main__":
    main(CompTimeDateCtl)
 0707010005ca43000081fd0000000000000000000000015ab21754000040a0000000660000000000000000000000000000003400000000root/var/lib/opensvc/compliance/com.opensvc/user.py   #!/usr/bin/env python

data = {
  "default_prefix": "OSVC_COMP_USER_",
  "example_value": """
{
  "tibco": {
    "shell": "/bin/ksh",
    "gecos":"agecos"
  },
  "tibco1": {
    "shell": "/bin/tcsh",
    "gecos": "another gecos"
  }
}
""",
  "description": """* Verify a local system user configuration
* A minus (-) prefix to the user name indicates the user should not exist

Environment variable modifying the object behaviour:
* OSVC_COMP_USERS_INITIAL_PASSWD=true|false
""",
  "form_definition": """
Desc: |
  A rule defining a list of Unix users and their properties. Used by the users and group_membership compliance objects.
Css: comp48
Outputs:
  -
    Dest: compliance variable
    Type: json
    Format: dict of dict
    Key: user
    EmbedKey: No
    Class: user
Inputs:
  -
    Id: user
    Label: User name
    DisplayModeLabel: user
    LabelCss: guy16
    Mandatory: Yes
    Type: string
    Help: The Unix user name.
  -
    Id: uid
    Label: User id
    DisplayModeLabel: uid
    LabelCss: guy16
    Mandatory: Yes
    Type: string or integer
    Help: The Unix uid of this user.
  -
    Id: gid
    Label: Group id
    DisplayModeLabel: gid
    LabelCss: guys16
    Mandatory: Yes
    Type: string or integer
    Help: The Unix principal gid of this user.
  -
    Id: shell
    Label: Login shell
    DisplayModeLabel: shell
    LabelCss: action16
    Type: string
    Help: The Unix login shell for this user.
  -
    Id: home
    Label: Home directory
    DisplayModeLabel: home
    LabelCss: action16
    Type: string
    Help: The Unix home directory full path for this user.
  -
    Id: password
    Label: Password hash
    DisplayModeLabel: pwd
    LabelCss: action16
    Type: string
    Help: The password hash for this user. It is recommanded to set it to '!!' or to set initial password to change upon first login. Leave empty to not check nor set the password.
  -
    Id: gecos
    Label: Gecos
    DisplayModeLabel: gecos
    LabelCss: action16
    Type: string
    Help: A one-line comment field describing the user.
  -
    Id: check_home
    Label: Enforce homedir ownership
    DisplayModeLabel: home ownership
    LabelCss: action16
    Type: string
    Default: yes
    Candidates:
      - "yes"
      - "no"
    Help: Toggles the user home directory ownership checking.
""",
}

import os
import sys
import json
import pwd
import re
from utilities import which

try:
    import spwd
    cap_shadow = True
except:
    cap_shadow = False

from subprocess import Popen, list2cmdline, PIPE

sys.path.append(os.path.dirname(__file__))

from comp import *

blacklist = [
 "root",
 "bin",
 "daemon",
 "adm",
 "lp",
 "sync",
 "shutdown",
 "halt",
 "mail",
 "news",
 "uucp",
 "operator",
 "nobody",
 "nscd",
 "vcsa",
 "pcap",
 "mailnull",
 "smmsp",
 "sshd",
 "rpc",
 "avahi",
 "rpcuser",
 "nfsnobody",
 "haldaemon",
 "avahi-autoipd",
 "ntp"
]

class CompUser(CompObject):
    def __init__(self, prefix=None):
        CompObject.__init__(self, prefix=prefix, data=data)

    def init(self):
        self.pwt = {
            'shell': 'pw_shell',
            'home': 'pw_dir',
            'uid': 'pw_uid',
            'gid': 'pw_gid',
            'gecos': 'pw_gecos',
            'password': 'pw_passwd',
        }
        self.spwt = {
            'spassword': 'sp_pwd',
        }
        self.usermod_p = {
            'shell': '-s',
            'home': '-d',
            'uid': '-u',
            'gid': '-g',
            'gecos': '-c',
            'password': '-p',
            'spassword': '-p',
        }
        self.sysname, self.nodename, x, x, self.machine = os.uname()

        if "OSVC_COMP_USERS_INITIAL_PASSWD" in os.environ and \
           os.environ["OSVC_COMP_USERS_INITIAL_PASSWD"] == "true":
            self.initial_passwd = True
        else:
            self.initial_passwd = False

        if self.sysname not in ['SunOS', 'Linux', 'HP-UX', 'AIX', 'OSF1', 'FreeBSD']:
            perror('module not supported on', self.sysname)
            raise NotApplicable()

        if self.sysname == "FreeBSD":
            self.useradd = ["pw", "useradd"]
            self.usermod = ["pw", "usermod"]
            self.userdel = ["pw", "userdel"]
        else:
            self.useradd = ["useradd"]
            self.usermod = ["usermod"]
            self.userdel = ["userdel"]

        self.users = {}
        for d in self.get_rules():
            for user in d:
                if user not in self.users:
                    self.users[user] = d[user]
                else:
                    for key in self.usermod_p.keys():
                        if key in d[user] and key not in self.users[user]:
                            self.users[user][key] = d[user][key]

        for user, d in self.users.items():
            for k in ('uid', 'gid'):
                if k in self.users[user]:
                    self.users[user][k] = int(d[k])

            if "password" in d and len(d["password"]) == 0:
                del(self.users[user]["password"])

            if cap_shadow:
                if "password" in d and len(d["password"]) > 0 and \
                   ("spassword" not in d or len(d["spassword"]) == 0):
                    self.users[user]["spassword"] = self.users[user]["password"]
                    del self.users[user]["password"]
                if "spassword" not in d:
                    self.users[user]["spassword"] = "x"
            else:
                if "spassword" in d and len(d["spassword"]) > 0 and \
                   ("password" not in d or len(d["password"]) == 0):
                    self.users[user]["password"] = self.users[user]["spassword"]
                    del self.users[user]["spassword"]
                if "password" not in d:
                    self.users[user]["password"] = "x"


    def fixable(self):
        if not which(self.usermod[0]):
            perror(self.usermod[0], "program not found")
            return RET_ERR
        return RET_OK

    def grpconv(self):
        if not cap_shadow or not os.path.exists('/etc/gshadow'):
            return
        if not which('grpconv'):
            return
        with open('/etc/group', 'r') as f:
            buff = f.read()
        l = []
        for line in buff.split('\n'):
            u = line.split(':')[0]
            if u in l:
                perror("duplicate group %s in /etc/group. skip grpconv (grpconv bug workaround)"%u)
                return
            l.append(u)
        p = Popen(['grpconv'])
        p.communicate()

    def pwconv(self):
        if not cap_shadow or not os.path.exists('/etc/shadow'):
            return
        if not which('pwconv'):
            return
        p = Popen(['pwconv'])
        p.communicate()

    def fix_item(self, user, item, target):
        if item in ["password", "spassword"]:
            if self.initial_passwd:
                pinfo("skip", user, "password modification in initial_passwd mode")
                return RET_OK
            if target == "x":
                return RET_OK
            if self.sysname in ("AIX"):
                return RET_OK
        cmd = [] + self.usermod
        if self.sysname == "FreeBSD":
            cmd.append(user)
        cmd += [self.usermod_p[item], str(target)]
        if item == 'home':
            cmd.append('-m')
        if self.sysname != "FreeBSD":
            cmd.append(user)
        pinfo(list2cmdline(cmd))
        p = Popen(cmd)
        out, err = p.communicate()
        r = p.returncode
        self.pwconv()
        self.grpconv()
        if r == 0:
            return RET_OK
        else:
            return RET_ERR

    def check_item(self, user, item, target, current, verbose=False):
        if type(current) == int and current < 0:
            current += 4294967296
        if sys.version_info[0] < 3 and type(current) == str and type(target) == unicode:
            current = unicode(current, errors="ignore")
        if target == current:
            if verbose:
                pinfo('user', user, item+':', current)
            return RET_OK
        elif "passw" in item and target == "!!" and current == "":
            if verbose:
                pinfo('user', user, item+':', current)
            return RET_OK
        else:
            if verbose:
                perror('user', user, item+':', current, 'target:', target)
            return RET_ERR

    def check_user_del(self, user, verbose=True):
        r = 0
        try:
            userinfo=pwd.getpwnam(user)
        except KeyError:
            if verbose:
                pinfo('user', user, 'does not exist, on target')
            return RET_OK
        if verbose:
            perror('user', user, "exists, shouldn't")
        return RET_ERR

    def check_user(self, user, props, verbose=True):
        if user.startswith('-'):
            return self.check_user_del(user.lstrip('-'), verbose=verbose)
        r = 0
        try:
            userinfo=pwd.getpwnam(user)
        except KeyError:
            if self.try_create_user(props):
                if verbose:
                    perror('user', user, 'does not exist')
                return RET_ERR
            else:
                if verbose:
                    perror('user', user, 'does not exist and not enough info to create it')
                return RET_ERR

        for prop in self.pwt:
            if prop in props:
                if prop == "password":
                    if self.initial_passwd:
                        if verbose:
                            pinfo("skip", user, "passwd checking in initial_passwd mode")
                        continue
                    if props[prop] == "x":
                        continue
                r |= self.check_item(user, prop, props[prop], getattr(userinfo, self.pwt[prop]), verbose=verbose)

        if 'check_home' not in props or props['check_home'] == "yes":
            r |= self.check_home_ownership(user, verbose=verbose)

        if not cap_shadow:
            return r

        try:
            usersinfo=spwd.getspnam(user)
        except KeyError:
            if "spassword" in props:
                if verbose:
                    perror(user, "not declared in /etc/shadow")
                r |= RET_ERR
            usersinfo = None

        if usersinfo is not None:
            for prop in self.spwt:
                if prop in props:
                    if prop == "spassword":
                        if self.initial_passwd:
                            if verbose:
                                pinfo("skip", user, "spasswd checking in initial_passwd mode")
                            continue
                        if props[prop] == "x":
                            continue
                    r |= self.check_item(user, prop, props[prop], getattr(usersinfo, self.spwt[prop]), verbose=verbose)

        return r

    def try_create_user(self, props):
        #
        # don't try to create user if passwd db is not 'files'
        # beware: 'files' db is the implicit default
        #
        if 'db' in props and props['db'] != 'files':
            return False
        return True

    def get_uid(self, user):
        import pwd
        try:
            info=pwd.getpwnam(user)
            uid = info[2]
        except:
            perror("user %s does not exist"%user)
            raise ComplianceError()
        return uid

    def check_home_ownership(self, user, verbose=True):
        path = os.path.expanduser("~"+user)
        if not os.path.exists(path):
            if verbose:
                perror(path, "homedir does not exist")
            return RET_ERR 
        tuid = self.get_uid(user)
        uid = os.stat(path).st_uid
        if uid != tuid:
            if verbose: perror(path, 'uid should be %s but is %s'%(str(tuid), str(uid)))
            return RET_ERR
        if verbose: pinfo(path, 'owner is', user)
        return RET_OK

    def fix_home_ownership(self, user):
        if self.check_home_ownership(user, verbose=False) == RET_OK:
            return RET_OK
        uid = self.get_uid(user)
        path = os.path.expanduser("~"+user)
        if not os.path.exists(path):
            if os.path.exists("/etc/skel"):
                cmd = ['cp', '-R', '/etc/skel/', path]
                pinfo(list2cmdline(cmd))
                p = Popen(cmd)
                out, err = p.communicate()
                r = p.returncode
                if r != 0:
                    return RET_ERR

                cmd = ['chown', '-R', str(uid), path]
                pinfo(list2cmdline(cmd))
                p = Popen(cmd)
                out, err = p.communicate()
                r = p.returncode
                if r != 0:
                    return RET_ERR
            else:
                os.makedirs(path)
                os.chown(path, uid, -1)
        return RET_OK

    def unlock_user(self, user):
        if self.sysname != "SunOS":
            return
        cmd = ["uname", "-r"]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            return
        if out.strip() == '5.8':
            unlock_opt = '-d'
        else:
            unlock_opt = '-u'
        cmd = ["passwd", unlock_opt, user]
        pinfo(list2cmdline(cmd))
        p = Popen(cmd)
        out, err = p.communicate()
        r = p.returncode
        if r == 0:
            return RET_OK
        else:
            return RET_ERR

    def create_user(self, user, props):
        cmd = [] + self.useradd
        if self.sysname == "FreeBSD":
            cmd += [user]
        for item in props:
            if item == "check_home":
                continue
            prop = str(props[item])
            if len(prop) == 0:
                continue
            if item.endswith("password") and self.sysname in ("AIX", "SunOS", "OSF1"):
                continue
            cmd = cmd + self.usermod_p[item].split() + [prop]
            if item == "home":
                cmd.append("-m")
        if self.sysname != "FreeBSD":
            cmd += [user]
        pinfo(list2cmdline(cmd))
        p = Popen(cmd)
        out, err = p.communicate()
        r = p.returncode
        if r == 0:
            if self.unlock_user(user) == RET_ERR:
                return RET_ERR
            return RET_OK
        else:
            return RET_ERR

    def fix_user_del(self, user):
        if user in blacklist:
            perror("delete", user, "... cowardly refusing")
            return RET_ERR
        cmd = self.userdel + [user]
        pinfo(list2cmdline(cmd))
        p = Popen(cmd)
        out, err = p.communicate()
        r = p.returncode
        if r == 0:
            return RET_OK
        else:
            return RET_ERR

    def fix_user(self, user, props):
        if user.startswith('-'):
            return self.fix_user_del(user.lstrip('-'))
        r = 0
        try:
            userinfo = pwd.getpwnam(user)
        except KeyError:
            if self.try_create_user(props):
                return self.create_user(user, props)
            else:
                pinfo('user', user, 'does not exist and not enough info to create it')
                return RET_OK

        for prop in self.pwt:
            if prop in props and \
               self.check_item(user, prop, props[prop], getattr(userinfo, self.pwt[prop])) != RET_OK:
                r |= self.fix_item(user, prop, props[prop])

        if 'check_home' not in props or props['check_home'] == "yes":
            r |= self.fix_home_ownership(user)

        if not cap_shadow:
            return r

        try:
            usersinfo = spwd.getspnam(user)
        except KeyError:
            if "spassword" in props:
                self.fix_item(user, "spassword", props["spassword"])
                usersinfo = spwd.getspnam(user)
            else:
                usersinfo = None

        if usersinfo is not None:
            for prop in self.spwt:
                if prop in props and \
                    self.check_item(user, prop, props[prop], getattr(usersinfo, self.spwt[prop])) != RET_OK:
                    r |= self.fix_item(user, prop, props[prop])
        return r

    def check(self):
        r = 0
        for user, props in self.users.items():
            r |= self.check_user(user, props)
        return r

    def fix(self):
        r = 0
        for user, props in self.users.items():
            if self.check_user(user, props, verbose=False) == RET_ERR:
                r |= self.fix_user(user, props)
        return r

if __name__ == "__main__":
    main(CompUser)

0707010005ca44000081b40000000000000000000000015ab21754000002f0000000660000000000000000000000000000003900000000root/var/lib/opensvc/compliance/com.opensvc/utilities.py  #!/usr/bin/env python

from __future__ import print_function
import os
import sys

def is_exe(fpath):
    """Returns True if file path is executable, False otherwize
    does not follow symlink
    """
    return os.path.exists(fpath) and os.access(fpath, os.X_OK)

def which(program):
    """Returns True if program is in PATH and executable, False
    otherwize
    """
    fpath, fname = os.path.split(program)
    if fpath and is_exe(program):
        return program
    for path in os.environ["PATH"].split(os.pathsep):
        exe_file = os.path.join(path, program)
        if is_exe(exe_file):
            return exe_file
    return None

if __name__ == "__main__":
    print("this file is for import into compliance objects", file=sys.stderr)

0707010005ca45000081fd0000000000000000000000015ab2175400000bac000000660000000000000000000000000000003b00000000root/var/lib/opensvc/compliance/com.opensvc/volume_file.py    #!/usr/bin/env python

from __future__ import print_function

data = {
  "default_prefix": "OSVC_COMP_VOLUME_FILE_",
  "example_value": """ 
{
  "path": "/some/path/to/file",
  "fmt": "root@corp.com		%%HOSTNAME%%@corp.com",
  "uid": 500,
  "gid": 500,
}
  """,
  "description": """* Verify and install file content in a docker volume spectified by the environment variable OPENSVC_VOL_PATH, automatically set by the fs.docker driver provisioner.
* paths are relative to the volume head
* Verify and set file or directory ownership and permission
* Directory mode is triggered if the path ends with /

Special wildcards::

  %%ENV:VARNAME%%	Any environment variable value
  %%HOSTNAME%%		Hostname
  %%SHORT_HOSTNAME%%	Short hostname

""",
  "form_definition": """
Desc: |
  A file rule, fed to the 'files' compliance object to create a directory or a file and set its ownership and permissions. For files, a reference content can be specified or pointed through an URL.
Css: comp48

Outputs:
  -
    Dest: compliance variable
    Class: file
    Type: json
    Format: dict

Inputs:
  -
    Id: path
    Label: Path
    DisplayModeLabel: path
    LabelCss: action16
    Mandatory: Yes
    Help: File path to install the reference content to. A path ending with '/' is treated as a directory and as such, its content need not be specified.
    Type: string

  -
    Id: mode
    Label: Permissions
    DisplayModeLabel: perm
    LabelCss: action16
    Help: "In octal form. Example: 644"
    Type: integer

  -
    Id: uid
    Label: Owner
    DisplayModeLabel: uid
    LabelCss: guy16
    Help: Either a user ID or a user name
    Type: string or integer

  -
    Id: gid
    Label: Owner group
    DisplayModeLabel: gid
    LabelCss: guy16
    Help: Either a group ID or a group name
    Type: string or integer

  -
    Id: ref
    Label: Content URL pointer
    DisplayModeLabel: ref
    LabelCss: loc
    Help: "Examples:
        http://server/path/to/reference_file
        https://server/path/to/reference_file
        ftp://server/path/to/reference_file
        ftp://login:pass@server/path/to/reference_file"
    Type: string

  -
    Id: fmt
    Label: Content
    DisplayModeLabel: fmt
    LabelCss: hd16
    Css: pre
    Help: A reference content for the file. The text can embed substitution variables specified with %%ENV:VAR%%.
    Type: text
"""
}

import os
import sys

sys.path.append(os.path.dirname(__file__))

from comp import *
from file import CompFiles

class CompVolumeFiles(CompFiles):
    def __init__(self, prefix=None):
        CompObject.__init__(self, prefix=prefix, data=data)

    def init(self):
        if "OPENSVC_VOL_PATH" not in os.environ:
            raise NotApplicable()
        CompFiles.init(self)
        self.vol_path = os.environ["OPENSVC_VOL_PATH"]
        for i, data in enumerate(self.files):
            self.files[i]["path"] = os.path.join(self.vol_path, data["path"].lstrip(os.sep))

if __name__ == "__main__":
    main(CompVolumeFiles)
0707010005ca48000081fd0000000000000000000000015ab2175400005a50000000660000000000000000000000000000003400000000root/var/lib/opensvc/compliance/com.opensvc/vuln.py   #!/usr/bin/env python

data = {
  "default_prefix": "OSVC_COMP_VULN_",
  "example_value": """ 
[
 {
  "pkgname": "kernel",
  "minver": "2.6.18-238.19.1.el5",
  "firstver": "2.6.18-238"
 },
 {
  "pkgname": "kernel-xen",
  "minver": "2.6.18-238.19.1.el5"
 }
]
  """,
  "description": """* Raise an alert if an installed package version is in a version range
* If the package is not installed, do not raise an alert
""",
  "form_definition": """
Desc: |
  A rule defining a list of vulnerable packages and their minimum release version fixing the vulnerability.

Css: comp48

Outputs:
  -
    Dest: compliance variable
    Type: json
    Format: list of dict
    Class: vuln

Inputs:
  -
    Id: pkgname
    Label: Package name
    DisplayModeLabel: pkgname
    LabelCss: pkg16
    Mandatory: Yes
    Type: string
    Help: The package name, as known to the target system's package manager.
  -
    Id: firstver
    Label: First vulnerable version
    DisplayModeLabel: firstver
    LabelCss: pkg16
    Mandatory: No
    Type: string
    Help: The first vulnerable package version. In the security context, the package version introducing the vulnerability.
  -
    Id: minver
    Label: Minimum version
    DisplayModeLabel: minver
    LabelCss: pkg16
    Mandatory: Yes
    Type: string
    Help: The package minimum version. In the security context, the package version fixing the vulnerability.

""" 
}

import os
import sys
import json
import pwd
import sys
import re
import tempfile
from subprocess import *
from distutils.version import LooseVersion as V
from utilities import which


sys.path.append(os.path.dirname(__file__))

from comp import *

def repl(matchobj):
    return '.0'+matchobj.group(0)[1:]

class LiveKernVulnerable(Exception):
    pass

class CompVuln(CompObject):
    def __init__(self, prefix=None, uri=None):
        CompObject.__init__(self, prefix=prefix, data=data)
        self.uri = uri

    def init(self):
        self.highest_avail_version = "0"
        self.fix_list = []
        self.need_pushpkg = False
        self.sysname, self.nodename, x, x, self.machine = os.uname()

        if 'OSVC_COMP_VULN_STRICT' in os.environ and \
           os.environ['OSVC_COMP_VULN_STRICT'] == "true":
            self.strict = True
        else:
            self.strict = False

        if 'OSVC_COMP_VULN_PKG_TYPE' in os.environ and \
           os.environ['OSVC_COMP_VULN_PKG_TYPE'] == "bundle":
            self.pkg_type = 'bundle'
        else:
            self.pkg_type = 'product'

        self.packages = []
        for k, rule in self.get_rule_items():
            try:
                self.packages += self.add_rule(k, rule)
            except InitError:
                continue
            except ValueError:
                perror('failed to parse variable', os.environ[k])

        if len(self.packages) == 0:
            raise NotApplicable()

        if self.sysname not in ['Linux', 'HP-UX', 'AIX', 'SunOS']:
            perror('module not supported on', self.sysname)
            raise NotApplicable()

        if 'OSVC_COMP_NODES_OS_VENDOR' not in os.environ:
            perror("OS_VENDOR is not set. Check your asset")
            raise NotApplicable()

        vendor = os.environ['OSVC_COMP_NODES_OS_VENDOR']
        if vendor in ['Debian', 'Ubuntu']:
            self.get_installed_packages = self.deb_get_installed_packages
            self.fix_pkg = self.apt_fix_pkg
            self.fixable_pkg = self.apt_fixable_pkg
            self.fix_all = None
        elif vendor in ['CentOS', 'Redhat', 'Red Hat'] or \
             (vendor == 'Oracle' and self.sysname == 'Linux'):
            self.get_installed_packages = self.rpm_get_installed_packages
            self.fix_pkg = self.yum_fix_pkg
            self.fixable_pkg = self.yum_fixable_pkg
            self.fix_all = None
        elif vendor in ['SuSE']:
            self.get_installed_packages = self.rpm_get_installed_packages
            self.fix_pkg = self.zyp_fix_pkg
            self.fixable_pkg = self.zyp_fixable_pkg
            self.fix_all = None
        elif vendor in ['HP']:
            if self.uri is None:
                perror("URI is not set")
                raise NotApplicable()
            self.get_installed_packages = self.hp_get_installed_packages
            self.fix_pkg = self.hp_fix_pkg
            self.fixable_pkg = self.hp_fixable_pkg
            self.fix_all = self.hp_fix_all
        elif vendor in ['IBM']:
            self.get_installed_packages = self.aix_get_installed_packages
            self.fix_pkg = self.aix_fix_pkg
            self.fixable_pkg = self.aix_fixable_pkg
            self.fix_all = None
        elif vendor in ['Oracle']:
            self.get_installed_packages = self.sol_get_installed_packages
            self.fix_pkg = self.sol_fix_pkg
            self.fixable_pkg = self.sol_fixable_pkg
            self.fix_all = None
        else:
            perror(vendor, "not supported")
            raise NotApplicable()

        self.installed_packages = self.get_installed_packages()

    def add_rule(self, k, o):
        o["rule"] = k.replace("OSVC_COMP_", "")
        return [o]

    def get_free(self, c):
        if not os.path.exists(c):
            return 0
        cmd = ["df", "-k", c]
        p = Popen(cmd, stdout=PIPE, stderr=None)
        out, err = p.communicate()
        out = bdecode(out)
        for line in out.split():
            if "%" in line:
                l = out.split()
                for i, w in enumerate(l):
                    if '%' in w:
                        break
                try:
                    f = int(l[i-1])
                    return f
                except:
                    return 0
        return 0

    def get_temp_dir(self):
        if hasattr(self, "tmpd"):
            return self.tmpd
        candidates = ["/tmp", "/var/tmp", "/root"]
        free = {}
        for c in candidates:
            free[self.get_free(c)] = c
        max = sorted(free.keys())[-1]
        self.tmpd = free[max]
        pinfo("selected %s as temp dir (%d KB free)" % (self.tmpd, max))
        return self.tmpd

    def download(self, pkg_name):
        import urllib
        import tempfile
        f = tempfile.NamedTemporaryFile(dir=self.get_temp_dir())
        dname = f.name
        f.close()
        try:
            os.makedirs(dname)
        except:
            pass
        fname = os.path.join(dname, "file")
        try:
            self.urlretrieve(pkg_name, fname)
        except IOError:
            try:
                os.unlink(fname)
                os.unlink(dname)
            except:
                pass
            raise Exception("download failed: %s" % str(e))
        import tarfile
        os.chdir(dname)
        try:
            tar = tarfile.open(fname)
        except:
            pinfo("not a tarball")
            return fname
        try:
            tar.extractall()
        except:
            try:
                os.unlink(fname)
                os.unlink(dname)
            except:
                pass
            # must be a pkg
            return dname
        tar.close()
        os.unlink(fname)
        return dname

    def get_os_ver(self):
        cmd = ['uname', '-v']
        p = Popen(cmd, stdout=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            return 0
        out = bdecode(out)
        lines = out.splitlines()
        if len(lines) == 0:
            return 0
        try:
            osver = float(lines[0])
        except:
            osver = 0
        return osver

    def sol_fix_pkg(self, pkg):
        r = self.check_pkg(pkg)
        if r == RET_OK:
            return RET_NA

        if 'repo' not in pkg or len(pkg['repo']) == 0:
            perror("no repo specified in the rule")
            return RET_NA

        pkg_url = pkg['repo']+"/"+pkg['pkgname']
        pinfo("download", pkg_url)
        try:
            dname = self.download(pkg_url)
        except Exception as e:
            perror(e)
            return RET_ERR

        if pkg["pkgname"] in self.installed_packages:
            os.chdir("/")
            yes = os.path.dirname(__file__) + "/yes"
            cmd = '%s | pkgrm %s' % (yes, pkg['pkgname'])
            print(cmd)
            r = os.system(cmd)
            if r != 0:
                return RET_ERR

        if os.path.isfile(dname):
            d = dname
        else:
            d = "."
            os.chdir(dname)

        if self.get_os_ver() < 10:
            opts = ''
        else:
            opts = '-G'

        if 'resp' in pkg and len(pkg['resp']) > 0:
            f = tempfile.NamedTemporaryFile(dir=self.get_temp_dir())
            resp = f.name
            f.close()
            with open(resp, "w") as f:
                f.write(pkg['resp'])
        else:
            resp = "/dev/null"
        yes = os.path.dirname(__file__) + "/yes"
        cmd = '%s | pkgadd -r %s %s -d %s all' % (yes, resp, opts, d)
        print(cmd)
        r = os.system(cmd)
        os.chdir("/")

        if os.path.isdir(dname):
            import shutil
            shutil.rmtree(dname)

        if r != 0:
            return RET_ERR
        return RET_OK

    def sol_fixable_pkg(self, pkg):
        return 0

    def sol_fix_all(self):
        return RET_NA

    def sol_get_installed_packages(self):
        p = Popen(['pkginfo', '-l'], stdout=PIPE)
        (out, err) = p.communicate()
        if p.returncode != 0:
            perror('can not fetch installed packages list')
            return {}
        out = bdecode(out)
        return self.sol_parse_pkginfo(out)

    def sol_parse_pkginfo(self, out):
        l = {}
        for line in out.split('\n'):
            v = line.split(':')
            if len(v) != 2:
                continue
            f = v[0].strip()
            if f == "PKGINST":
                pkgname = v[1].strip()
            elif f == "ARCH":
                pkgarch = v[1].strip()
            elif f == "VERSION":
                pkgvers = v[1].strip()
                if pkgname in l:
                    l[pkgname] += [(pkgvers, pkgarch)]
                else:
                    l[pkgname] = [(pkgvers, pkgarch)]
        return l

    def aix_fix_pkg(self, pkg):
        r = self.check_pkg(pkg)
        if r == RET_OK:
            return RET_NA

        cmd = ['nimclient', '-o', 'cust',
               '-a', 'lpp_source=%s'%self.uri,
               '-a', 'installp_flags=aFQY',
               '-a', 'filesets=%s'%pkg['pkgname']]
        s = " ".join(cmd)
        pinfo(s)
        r = os.system(s)
        if r != 0:
            return RET_ERR
        return RET_OK

    def aix_fixable_pkg(self, pkg):
        return RET_NA

    def aix_fix_all(self):
        return RET_NA

    def aix_get_installed_packages(self):
        p = Popen(['lslpp', '-L', '-c'], stdout=PIPE)
        (out, err) = p.communicate()
        if p.returncode != 0:
            perror('can not fetch installed packages list')
            return {}
        out = bdecode(out)
        return self.aix_parse_lslpp(out)

    def aix_parse_lslpp(self, out):
        l = {}
        for line in out.split('\n'):
            if line.startswith('#') or len(line) == 0:
                continue
            v = line.split(':')
            if len(v) < 3:
                continue
            pkgname = v[1].replace('-'+v[2], '')
            if pkgname in l:
                l[pkgname] += [(v[2], "")]
            else:
                l[pkgname] = [(v[2], "")]
        return l

    def hp_fix_pkg(self, pkg):
        if self.check_pkg(pkg, verbose=False) == RET_OK:
            return RET_OK
        if self.fixable_pkg(pkg) == RET_ERR:
            return RET_ERR
        if self.highest_avail_version == "0":
            return RET_ERR
        if self.strict:
            self.fix_list.append(pkg["pkgname"]+',r='+pkg["minver"])
        else:
            self.fix_list.append(pkg["pkgname"]+',r='+self.highest_avail_version)
        self.need_pushpkg = True
        self.installed_packages = self.get_installed_packages()
        return RET_OK

    def hp_fix_all(self):
        r = call(['swinstall', '-x', 'allow_downdate=true', '-x', 'autoreboot=true', '-x', 'mount_all_filesystems=false', '-s', self.uri] + self.fix_list)
        if r != 0:
            return RET_ERR
        return RET_OK

    def hp_fixable_pkg(self, pkg):
        self.highest_avail_version = "0"
        if self.check_pkg(pkg, verbose=False) == RET_OK:
            return RET_OK
        cmd = ['swlist', '-l', self.pkg_type, '-s', self.uri, pkg['pkgname']]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        (out, err) = p.communicate()
        if p.returncode != 0:
            if "not found on host" in err:
                perror('%s > %s not available in repositories'%(pkg['pkgname'], pkg['minver']))
            else:
                perror('can not fetch available packages list')
            return RET_ERR
        out = bdecode(out)
        l = self.hp_parse_swlist(out)
        if len(l) == 0:
            perror('%s > %s not available in repositories'%(pkg['pkgname'], pkg['minver']))
            return RET_ERR
        for v in map(lambda x: x[0], l.values()[0]):
            if V(v) > V(self.highest_avail_version):
                self.highest_avail_version = v
        if V(self.highest_avail_version) < V(pkg['minver']):
            perror('%s > %s not available in repositories'%(pkg['pkgname'], pkg['minver']))
            return RET_ERR
        return RET_OK

    def hp_get_installed_packages(self):
        p = Popen(['swlist', '-l', self.pkg_type], stdout=PIPE)
        (out, err) = p.communicate()
        if p.returncode != 0:
            perror('can not fetch installed packages list')
            return {}
        out = bdecode(out)
        return self.hp_parse_swlist(out)

    def hp_parse_swlist(self, out):
        l = {}
        for line in out.split('\n'):
            if line.startswith('#') or len(line) == 0:
                continue
            v = line.split()
            if len(v) < 2:
                continue
            if v[0] in l:
                l[v[0]] += [(v[1], "")]
            else:
                l[v[0]] = [(v[1], "")]
        return l

    def rpm_get_installed_packages(self):
        p = Popen(['rpm', '-qa', '--qf', '%{NAME} %{VERSION}-%{RELEASE} %{ARCH}\n'], stdout=PIPE)
        (out, err) = p.communicate()
        if p.returncode != 0:
            perror('can not fetch installed packages list')
            return {}
        l = {}
        out = bdecode(out)
        for line in out.splitlines():
            v = line.split(' ')
            if len(v) != 3:
                continue
            if v[0] in l:
                l[v[0]] += [(v[1], v[2])]
            else:
                l[v[0]] = [(v[1], v[2])]
        return l

    def deb_get_installed_packages(self):
        p = Popen(['dpkg', '-l'], stdout=PIPE)
        (out, err) = p.communicate()
        if p.returncode != 0:
            perror('can not fetch installed packages list')
            return {}
        l = {}
        out = bdecode(out)
        for line in out.splitlines():
            if not line.startswith('ii'):
                continue
            v = line.split()[1:3]
            pkgname = v[0]
            pkgname = pkgname.split(':')[0]
            l[pkgname] = [(v[1], "")]
        return l

    def apt_fixable_pkg(self, pkg):
        # TODO
        return RET_NA

    def zyp_fixable_pkg(self, pkg):
        return RET_NA

    def yum_fixable_pkg(self, pkg):
        try:
            r = self.check_pkg(pkg, verbose=False)
        except LiveKernVulnerable:
            r = RET_OK

        if r == RET_OK:
            return RET_OK

        cmd = ['yum', 'list', 'available', pkg['pkgname']]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        (out, err) = p.communicate()
        if p.returncode != 0:
            if "No matching Packages" in err:
                perror('%s > %s not available in repositories'%(pkg['pkgname'], pkg['minver']))
            else:
                perror('can not fetch available packages list')
            return RET_ERR
        highest_avail_version = "0"
        out = bdecode(out)
        for line in out.splitlines():
            l = line.split()
            if len(l) != 3:
                continue
            if V(l[1]) > V(highest_avail_version):
                highest_avail_version = l[1]
        if V(highest_avail_version) < V(pkg['minver']):
            perror('%s > %s not available in repositories'%(pkg['pkgname'], pkg['minver']))
            return RET_ERR
        return RET_OK

    def tainted(self, pkg):
        if not pkg["pkgname"].startswith("kernel-") and \
           not pkg["pkgname"].startswith("linux-image"):
            return False
        if self.sysname != 'Linux':
            return False
        if not os.path.exists("/proc/sys/kernel/tainted"):
            return False
        with open("/proc/sys/kernel/tainted", "r") as f:
            buff = f.read()
        if buff == "0":
            return False
        return True

    def zyp_fix_pkg(self, pkg):
        try:
            r = self.check_pkg(pkg, verbose=False)
        except LiveKernVulnerable:
            r = RET_OK
        if r == RET_OK:
            return RET_OK
        if self.fixable_pkg(pkg) == RET_ERR:
            return RET_ERR
        r = call(['zypper', 'install', '-y', pkg["pkgname"]])
        if r != 0:
            return RET_ERR
        self.need_pushpkg = True
        self.installed_packages = self.get_installed_packages()
        return RET_OK

    def yum_fix_pkg(self, pkg):
        try:
            r = self.check_pkg(pkg, verbose=False)
        except LiveKernVulnerable:
            r = RET_OK
        if r == RET_OK:
            return RET_OK
        if self.fixable_pkg(pkg) == RET_ERR:
            return RET_ERR
        r = call(['yum', '-y', 'install', pkg["pkgname"]])
        if r != 0:
            return RET_ERR
        self.need_pushpkg = True
        self.installed_packages = self.get_installed_packages()
        return RET_OK

    def apt_fix_pkg(self, pkg):
        if self.check_pkg(pkg, verbose=False) == RET_OK:
            return RET_OK
        r = call(['apt-get', 'install', '-y', '--allow-unauthenticated', pkg["pkgname"]])
        if r != 0:
            return RET_ERR
        self.need_pushpkg = True
        self.installed_packages = self.get_installed_packages()
        return RET_OK

    def get_raw_kver(self):
        return os.uname()[2]

    def get_kver(self):
        s = self.get_raw_kver()
        s = s.replace('xen', '')
        s = s.replace('hugemem', '')
        s = s.replace('smp', '')
        s = s.replace('PAE', '')
        s = s.replace('.x86_64','')
        s = s.replace('.i686','')
        return s

    def workaround_python_cmp(self, s):
        """ python list cmp says a > 9, but rpm says z < 0, ie :
            python says 2.6.18-238.el5 > 2.6.18-238.11.1.el5
            which is wrong in the POV of the package manager.

            replace .[a-z]* by .00000000[a-z] to force the desired behaviour
        """
        return re.sub("\.[a-zA-Z]+", repl, s)

    def check_pkg(self, pkg, verbose=True):
        if not pkg["pkgname"] in self.installed_packages:
            if verbose:
                pinfo(pkg["pkgname"], "is not installed (%s:not applicable)"%pkg["rule"])
            return RET_OK

        name = pkg["pkgname"]

        if name.startswith("kernel"):
            if self.tainted(pkg):
                pinfo(name, "booted kernel is tainted", "(%s)"%pkg["rule"])
            kver = self.get_raw_kver()
            for i in ('xen', 'hugemem', 'smp', 'PAE'):
                if kver.endswith(i) and name != "kernel-"+i:
                    if verbose:
                        pinfo(name, "bypassed :", i, "kernel booted", "(%s:not applicable)"%pkg["rule"])
                    return RET_OK

        r = RET_OK
        max = "0"
        max_v = V(max)
        ok = []
        minver = self.workaround_python_cmp(pkg['minver'])
        target = V(minver)
        if 'firstver' in pkg and pkg['firstver'] != "":
            firstver = self.workaround_python_cmp(pkg['firstver'])
        else:
            firstver = "0"
        firstver_v = V(firstver)
        candidates = map(lambda x: [name]+list(x), self.installed_packages[name])

        for _name, vers, arch in candidates:
            _vers = self.workaround_python_cmp(vers)
            actual = V(_vers)
            if actual > max_v or max == "0":
                max = vers
                max_v = actual
            if target <= actual or firstver_v > actual:
                ok.append((_name, vers, arch))

        if max == "0":
            # not installed
            if verbose:
                pinfo(name, "is not installed (%s:not applicable)"%pkg["rule"])
            return RET_OK

        if name.startswith("kernel"):
            kver = self.get_kver()
            if len(ok) == 0:
                if verbose:
                    perror(', '.join(map(lambda x: x[0]+"-"+x[1]+"."+x[2], candidates)), 'installed and vulnerable. upgrade to', pkg["minver"], "(%s:need upgrade)"%pkg["rule"])
                return RET_ERR
            elif kver not in map(lambda x: x[1], ok):
                if verbose:
                    perror(', '.join(map(lambda x: x[0]+"-"+x[1]+"."+x[2], ok)), "installed and not vulnerable but vulnerable kernel", self.get_raw_kver(), "booted", "(%s:need reboot)"%pkg["rule"])
                raise LiveKernVulnerable()
            else:
                if verbose:
                    pinfo("kernel", self.get_raw_kver(), "installed, booted and not vulnerable", "(%s:not vulnerable)"%pkg["rule"])
                return RET_OK

        if len(ok) > 0:
            if verbose:
                pinfo("%s installed and not vulnerable (%s:not vulnerable)"%(', '.join(map(lambda x: x[0]+"-"+x[1]+"."+x[2], ok)), pkg["rule"]))
            return RET_OK

        if verbose:
            perror('package', name+"-"+vers, 'is vulnerable. upgrade to', pkg["minver"], "(%s:need upgrade)"%pkg["rule"])
        return RET_ERR

    def check(self):
        r = 0
        for pkg in self.packages:
            try:
                _r = self.check_pkg(pkg)
                r |= _r
            except LiveKernVulnerable:
                r |= RET_ERR
        return r

    def fix(self):
        r = 0
        for pkg in self.packages:
            if self.tainted(pkg):
                perror(name, "booted kernel is tainted. not safe to upgrade.", "(%s)"%pkg["rule"])
            r |= self.fix_pkg(pkg)
        if self.fix_all is not None and len(self.fix_list) > 0:
            self.fix_all()
        if self.need_pushpkg:
            self.pushpkg()
        return r

    def pushpkg(self):
        bin = 'nodemgr'
        if which(bin) is None:
            return
        cmd = [bin, 'pushpkg']
        pinfo(' '.join(cmd))
        p = Popen(cmd)
        p.communicate()

    def fixable(self):
        r = 0
        for pkg in self.packages:
            r |= self.fixable_pkg(pkg)
        return r

if __name__ == "__main__":
    main(CompVuln)

0707010005ca49000081fd0000000000000000000000015ab217540000158a000000660000000000000000000000000000003600000000root/var/lib/opensvc/compliance/com.opensvc/xinetd.py #!/usr/bin/env python

data = {
  "default_prefix": "OSVC_COMP_XINETD_",
  "example_value": """
{
  "gssftp": {
    "disable": "no",
    "server_args": "-l -a -u 022"
  }
}""",
  "description": """* Setup and verify a xinetd service configuration
""",
  "form_definition": """
Desc: |
  A rule defining how a xinetd service should be configured

Inputs:
  -
    Id: xinetdsvc
    Label: Service Name
    DisplayModeLabel: service
    LabelCss: action16
    Mandatory: Yes
    Help: The xinetd service name, ie the service file name in /etc/xinetd.d/
    Type: string
  -
    Id: disable
    Label: Disable 
    DisplayModeLabel: Disable
    LabelCss: action16
    Help: Defines if the xinetd service target state is enabled or disabled
    Type: string
    Default: yes
    Candidates:
      - "yes"
      - "no"
  -
    Id: server_args
    Label: Server Args
    DisplayModeLabel: args
    LabelCss: action16
    Help: Command line parameter to pass to the service's server executable
    Type: string
""",
}

import os
import sys
import json
import pwd

sys.path.append(os.path.dirname(__file__))

from comp import *

class Xinetd(CompObject):
    def __init__(self, prefix=None):
        CompObject.__init__(self, prefix=prefix, data=data)

    def init(self):
        self.base = os.path.join(os.sep, "etc", "xinetd.d")
        if not os.path.exists(self.base):
            perror(self.base, 'does not exist')
            raise NotApplicable()

        self.svcs = {}
        for d in self.get_rules():
            self.svcs.update(d)

        if len(self.svcs) == 0:
            raise NotApplicable()

        self.cf_d = {}
        self.known_props = (
            "flags",
            "socket_type",
            "wait",
            "user",
            "server",
            "server_args",
            "disable")

    def fixable(self):
        return RET_NA

    def get_svc(self, svc):
        if svc in self.cf_d:
            return self.cf_d[svc]

        p = os.path.join(self.base, svc)
        if not os.path.exists(p):
            self.cf_d[svc] = {}
            return self.cf_d[svc]

        if svc not in self.cf_d:
            self.cf_d[svc] = {}

        with open(p, 'r') as f:
            for line in f.read().split('\n'):
                if '=' not in line:
                    continue
                l = line.split('=')
                if len(l) != 2:
                    continue
                var = l[0].strip()
                val = l[1].strip()
                self.cf_d[svc][var] = val

        return self.cf_d[svc]

    def fix_item(self, svc, item, target):
        if item not in self.known_props:
            perror('xinetd service', svc, item+': unknown property in compliance rule')
            return RET_ERR
        cf = self.get_svc(svc)

        if item in cf and cf[item] == target:
            return RET_OK

        p = os.path.join(self.base, svc)
        if not os.path.exists(p):
            perror(p, "does not exist")
            return RET_ERR

        done = False
        with open(p, 'r') as f:
            buff = f.read().split('\n')
            for i, line in enumerate(buff):
                if '=' not in line:
                    continue
                l = line.split('=')
                if len(l) != 2:
                    continue
                var = l[0].strip()
                if var != item:
                    continue
                l[1] = target
                buff[i] = "= ".join(l)
                done = True

        if not done:
            with open(p, 'r') as f:
                buff = f.read().split('\n')
                for i, line in enumerate(buff):
                    if '=' not in line:
                        continue
                    l = line.split('=')
                    if len(l) != 2:
                        continue
                    buff.insert(i, item+" = "+target)
                    done = True
                    break

        if not done:
            perror("failed to set", item, "=", target, "in", p)
            return RET_ERR

        with open(p, 'w') as f:
            f.write("\n".join(buff))

        pinfo("set", item, "=", target, "in", p)
        return RET_OK

    def check_item(self, svc, item, target, verbose=False):
        if item not in self.known_props:
            perror('xinetd service', svc, item+': unknown property in compliance rule')
            return RET_ERR
        cf = self.get_svc(svc)
        if item in cf and target == cf[item]:
            if verbose:
                pinfo('xinetd service', svc, item+':', cf[item])
            return RET_OK
        elif item in cf:
            if verbose:
                perror('xinetd service', svc, item+':', cf[item], 'target:', target)
        else:
            if verbose:
                perror('xinetd service', svc, item+': unset', 'target:', target)
        return RET_ERR

    def check_svc(self, svc, props):
        r = 0
        for prop in props:
            r |= self.check_item(svc, prop, props[prop], verbose=True)
        return r

    def fix_svc(self, svc, props):
        r = 0
        for prop in props:
            r |= self.fix_item(svc, prop, props[prop])
        return r

    def check(self):
        r = 0
        for svc, props in self.svcs.items():
            r |= self.check_svc(svc, props)
        return r

    def fix(self):
        r = 0
        for svc, props in self.svcs.items():
            r |= self.fix_svc(svc, props)
        return r

if __name__ == "__main__":
    main(Xinetd)
  0707010005ca4a000081fd0000000000000000000000015ab2175400000079000000660000000000000000000000000000003000000000root/var/lib/opensvc/compliance/com.opensvc/yes   #!/usr/bin/env python

from __future__ import print_function

try:
    while True:
        print("yes")
except:
    pass
   0707010005ca4b000081fd0000000000000000000000015ab217540000226e000000660000000000000000000000000000003300000000root/var/lib/opensvc/compliance/com.opensvc/zfs.py    #!/usr/bin/env python
data = {
  "default_prefix": "OSVC_COMP_ZFS_",
  "example_value": """ 
[
 {
  "name": "rpool/swap",
  "prop": "aclmode",
  "op": "=",
  "value": "discard"
 },
 {
  "name": "rpool/swap",
  "prop": "copies",
  "op": "<",
  "value": 1
 },
 {
  "name": "rpool/swap",
  "prop": "copies",
  "op": ">",
  "value": 0
 },
 {
  "name": "rpool/swap",
  "prop": "copies",
  "op": "<=",
  "value": 1
 },
 {
  "name": "rpool/swap",
  "prop": "copies",
  "op": ">=",
  "value": 1
 }
]
""",
  "description": """* Check the properties values against their target and operator
* The collector provides the format with wildcards.
* The module replace the wildcards with contextual values.
* In the 'fix' the zfs dataset property is set.
""",
  "form_definition": """
Desc: |
  A rule to set a list of zfs properties.
Css: comp48

Outputs:
  -
    Dest: compliance variable
    Type: json
    Format: list of dict
    Class: zfs dataset

Inputs:
  -
    Id: name
    Label: Dataset Name
    DisplayModeLabel: dsname
    LabelCss: hd16
    Mandatory: Yes
    Type: string
    Help: The zfs dataset name whose property to check.
  -
    Id: prop
    Label: Property
    DisplayModeLabel: property
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The zfs dataset property to check.
    Candidates:
      - aclinherit
      - aclmode
      - atime
      - canmount
      - checksum
      - compression
      - copies
      - dedup
      - devices
      - exec
      - keychangedate
      - keysource
      - logbias
      - mountpoint
      - nbmand
      - primarycache
      - quota
      - readonly
      - recordsize
      - refquota
      - refreservation
      - rekeydate
      - reservation
      - rstchown
      - secondarycache
      - setuid
      - share.*
      - snapdir
      - sync
      - vscan
      - xattr
      - zoned
  -
    Id: op_s
    Key: op
    Label: Comparison operator
    DisplayModeLabel: op
    LabelCss: action16
    Type: info
    Default: "="
    ReadOnly: yes
    Help: The comparison operator to use to check the property current value.
    Condition: "#prop != copies"
  -
    Id: op_n
    Key: op
    Label: Comparison operator
    DisplayModeLabel: op
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Default: "="
    StrictCandidates: yes
    Candidates:
      - "="
      - ">"
      - ">="
      - "<"
      - "<="
    Help: The comparison operator to use to check the property current value.
    Condition: "#prop == copies"
  -
    Id: value_on_off
    Key: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The zfs dataset property target value.
    Condition: "#prop IN sharenfs,sharesmb"
    StrictCandidates: yes
    Candidates:
      - "on"
      - "off"
  -
    Id: value_on_off_strict
    Key: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The zfs dataset property target value.
    Condition: "#prop IN canmount,atime,readonly,exec,devices,setuid,vscan,xattr,jailed,utf8only"
    StrictCandidates: yes
    Candidates:
      - "on"
      - "off"
  -
    Id: value_n
    Key: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: integer
    Help: The zfs dataset property target value.
    Condition: "#prop IN copies,recordsize,volsize"
  -
    Id: value_s
    Key: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The zfs dataset property target value.
    Condition: "#prop NOT IN normalization,casesensitivity,sync,volmode,logbias,snapdir,dedup,primarycache,secondarycache,redundant_metadata,checksum,compression,aclinherit,aclmode,copies,recordsize,volsize,canmount,atime,readonly,exec,devices,setuid,vscan,xattr,jailed,utf8only,sharenfs,sharesmb"
  -
    Id: value_aclinherit
    Key: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The zfs dataset property target value.
    Condition: "#prop == aclinherit"
    StrictCandidates: yes
    Candidates:
      - "discard"
      - "noallow"
      - "restricted"
      - "passthrough"
      - "passthrough-x"
  -
    Id: value_aclmode
    Key: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The zfs dataset property target value.
    Condition: "#prop == aclmode"
    StrictCandidates: yes
    Candidates:
      - "discard"
      - "groupmask"
      - "passthrough"
      - "restricted"
  -
    Id: value_checksum
    Key: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The zfs dataset property target value.
    Condition: "#prop == checksum"
    StrictCandidates: yes
    Candidates:
      - "on"
      - "off"
      - "fletcher2"
      - "fletcher4"
      - "sha256"
      - "noparity"
  -
    Id: value_compression
    Key: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The zfs dataset property target value.
    Condition: "#prop == compression"
    StrictCandidates: yes
    Candidates:
      - "on"
      - "off"
      - "lzjb"
      - "gzip"
      - "gzip-1"
      - "gzip-2"
      - "gzip-3"
      - "gzip-4"
      - "gzip-5"
      - "gzip-6"
      - "gzip-7"
      - "gzip-8"
      - "gzip-9"
      - "zle"
      - "lz4"
  -
    Id: value_dedup
    Key: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The zfs dataset property target value.
    Condition: "#prop == dedup"
    StrictCandidates: yes
    Candidates:
      - "on"
      - "off"
      - "verify"
      - "sha256"
      - "sha256,verify"
  -
    Id: value_primarycache
    Key: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The zfs dataset property target value.
    Condition: "#prop IN primarycache,secondarycache"
    StrictCandidates: yes
    Candidates:
      - "all"
      - "none"
      - "metadata"
  -
    Id: value_redundant_metadata
    Key: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The zfs dataset property target value.
    Condition: "#prop == redundant_metadata"
    StrictCandidates: yes
    Candidates:
      - "all"
      - "most"
  -
    Id: value_logbias
    Key: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The zfs dataset property target value.
    Condition: "#prop == logbias"
    StrictCandidates: yes
    Candidates:
      - "latency"
      - "throughput"
  -
    Id: value_snapdir
    Key: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The zfs dataset property target value.
    Condition: "#prop == snapdir"
    StrictCandidates: yes
    Candidates:
      - "hidden"
      - "visible"
  -
    Id: value_sync
    Key: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The zfs dataset property target value.
    Condition: "#prop == sync"
    StrictCandidates: yes
    Candidates:
      - "standard"
      - "always"
      - "disabled"
  -
    Id: value_volmode
    Key: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The zfs dataset property target value.
    Condition: "#prop == volmode"
    StrictCandidates: yes
    Candidates:
      - "default"
      - "geom"
      - "dev"
      - "none"
  -
    Id: value_casesensitivity
    Key: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The zfs dataset property target value.
    Condition: "#prop == casesensitivity"
    StrictCandidates: yes
    Candidates:
      - "sensitive"
      - "insensitive"
      - "mixed"
  -
    Id: value_normalization
    Key: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The zfs dataset property target value.
    Condition: "#prop == normalization"
    StrictCandidates: yes
    Candidates:
      - "none"
      - "formC"
      - "formD"
      - "formKC"
      - "formKD"
"""
}

import os
import sys

sys.path.append(os.path.dirname(__file__))

from zprop import *

class CompZfs(CompZprop):
    def __init__(self, prefix='OSVC_COMP_ZFS_'):
        CompObject.__init__(self, prefix=prefix, data=data)
        self.zbin = "zfs"

if __name__ == "__main__":
    main(CompZfs)

  0707010005ca4d000081fd0000000000000000000000015ab21754000015aa000000660000000000000000000000000000003500000000root/var/lib/opensvc/compliance/com.opensvc/zpool.py  #!/usr/bin/env python
data = {
  "default_prefix": "OSVC_COMP_ZPOOL_",
  "example_value": """ 
[
 {
  "name": "rpool",
  "prop": "failmode",
  "op": "=",
  "value": "continue"
 },
 {
  "name": "rpool",
  "prop": "dedupditto",
  "op": "<",
  "value": 1
 },
 {
  "name": "rpool",
  "prop": "dedupditto",
  "op": ">",
  "value": 0
 },
 {
  "name": "rpool",
  "prop": "dedupditto",
  "op": "<=",
  "value": 1
 },
 {
  "name": "rpool",
  "prop": "dedupditto",
  "op": ">=",
  "value": 1
 }
]
""",
  "description": """* Check the properties values against their target and operator
* The collector provides the format with wildcards.
* The module replace the wildcards with contextual values.
* In the 'fix' the zpool property is set.
""",
  "form_definition": """
Desc: |
  A rule to set a list of zpool properties.
Css: comp48

Outputs:
  -
    Dest: compliance variable
    Type: json
    Format: list of dict
    Class: zpool

Inputs:
  -
    Id: name
    Label: Pool Name
    DisplayModeLabel: poolname
    LabelCss: hd16
    Mandatory: Yes
    Type: string
    Help: The zpool name whose property to check.
  -
    Id: prop
    Label: Property
    DisplayModeLabel: property
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The zpool property to check.
    Candidates:
      - readonly
      - autoexpand
      - autoreplace
      - bootfs
      - cachefile
      - dedupditto
      - delegation
      - failmode
      - listshares
      - listsnapshots
      - version

  -
    Id: op_s
    Key: op
    Label: Comparison operator
    DisplayModeLabel: op
    LabelCss: action16
    Type: info
    Default: "="
    ReadOnly: yes
    Help: The comparison operator to use to check the property current value.
    Condition: "#prop IN readonly,autoexpand,autoreplace,bootfs,cachefile,delegation,failmode,listshares,listsnapshots"
  -
    Id: op_n
    Key: op
    Label: Comparison operator
    DisplayModeLabel: op
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Default: "="
    StrictCandidates: yes
    Candidates:
      - "="
      - ">"
      - ">="
      - "<"
      - "<="
    Help: The comparison operator to use to check the property current value.
    Condition: "#prop IN version,dedupditto"

  -
    Id: value_readonly
    Key: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The zpool property target value.
    Condition: "#prop == readonly"
    StrictCandidates: yes
    Candidates:
      - "on"
      - "off"
  -
    Id: value_autoexpand
    Key: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The zpool property target value.
    Condition: "#prop == autoexpand"
    StrictCandidates: yes
    Candidates:
      - "on"
      - "off"
  -
    Id: value_autoreplace
    Key: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The zpool property target value.
    Condition: "#prop == autoreplace"
    StrictCandidates: yes
    Candidates:
      - "on"
      - "off"
  -
    Id: value_delegation
    Key: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The zpool property target value.
    Condition: "#prop == delegation"
    StrictCandidates: yes
    Candidates:
      - "on"
      - "off"
  -
    Id: value_listshares
    Key: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The zpool property target value.
    Condition: "#prop == listshares"
    StrictCandidates: yes
    Candidates:
      - "on"
      - "off"
  -
    Id: value_listsnapshots
    Key: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The zpool property target value.
    Condition: "#prop == listsnapshots"
    StrictCandidates: yes
    Candidates:
      - "on"
      - "off"
  -
    Id: value_failmode
    Key: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The zpool property target value.
    Condition: "#prop == failmode"
    StrictCandidates: yes
    Candidates:
      - "continue"
      - "wait"
      - "panic"
  -
    Id: value_bootfs
    Key: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The zpool property target value.
    Condition: "#prop == bootfs"
  -
    Id: value_cachefile
    Key: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The zpool property target value.
    Condition: "#prop == cachefile"
  -
    Id: value_dedupditto
    Key: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: integer
    Help: The zpool property target value.
    Condition: "#prop == dedupditto"
  -
    Id: value_version
    Key: value
    Label: Value
    DisplayModeLabel: value
    LabelCss: action16
    Mandatory: Yes
    Type: integer
    Help: The zpool property target value.
    Condition: "#prop == version"
"""
}

import os
import sys

sys.path.append(os.path.dirname(__file__))

from zprop import *

class CompZpool(CompZprop):
    def __init__(self, prefix='OSVC_COMP_ZPOOL_'):
        CompObject.__init__(self, prefix=prefix, data=data)
        self.zbin = "zpool"

if __name__ == "__main__":
    main(CompZpool)

  0707010005ca4f000081fd0000000000000000000000015ab217540000113d000000660000000000000000000000000000003500000000root/var/lib/opensvc/compliance/com.opensvc/zprop.py  #!/usr/bin/env python

import os
import sys

sys.path.append(os.path.dirname(__file__))

from utilities import which
from comp import *
from subprocess import *

class CompZprop(CompObject):
    def __init__(self, prefix='OSVC_COMP_ZPROP_'):
        CompObject.__init__(self, prefix=prefix, data=data)

    def init(self):
        self.sysname, self.nodename, x, x, self.machine = os.uname()
        self.data = []

        for rule in self.get_rules():
            try:
                self.data += self.add_rule(rule)
            except InitError:
                continue
            except ValueError:
                perror('failed to parse variable', rule)

    def add_rule(self, d):
        allgood = True
        for k in ["name", "prop", "op", "value"]:
            if k not in d:
                perror('the', k, 'key should be in the dict:', d)
                allgood = False
        if allgood:
            return [d]
        return []

    def get_prop(self, d):
        cmd = [self.zbin, "get", d.get("prop"), d.get("name")]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            return
        out = bdecode(out)
        l = [line for line in out.splitlines() if line != ""]
        if len(l) != 2:
            return
        v1 = l[0].split()
        v2 = l[1].split()
        if len(v1) != len(v2):
            return
        data = {}
        for k, v in zip(v1, v2):
            data[k] = v
        return data

    def check_le(self, current, target):
        current = int(current)
        if current <= target:
            return RET_OK
        return RET_ERR

    def check_ge(self, current, target):
        current = int(current)
        if current >= target:
            return RET_OK
        return RET_ERR

    def check_lt(self, current, target):
        current = int(current)
        if current < target:
            return RET_OK
        return RET_ERR

    def check_gt(self, current, target):
        current = int(current)
        if current > target:
            return RET_OK
        return RET_ERR

    def check_eq(self, current, target):
        if current == str(target):
            return RET_OK
        return RET_ERR

    def fixable(self):
        return RET_NA

    def fix_zprop(self, d):
        if self.check_zprop(d) == RET_OK:
            return RET_OK
        prop = d.get("prop")
        target = d.get("value")
        name = d.get("name")
        cmd = [self.zbin, "set", prop+"="+target, name]
        pinfo(" ".join(cmd))
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            err = bdecode(err)
            if len(err) > 0:
                perror(err)
            return RET_ERR
        return RET_OK

    def check_zprop(self, d, verbose=False):
        v = self.get_prop(d)
        prop = d.get("prop")
        if v is None:
            if verbose:
                perror("property", prop, "does not exist")
            return RET_ERR
        current = v["VALUE"]
        op = d.get("op")
        target = d.get("value")
        if op == "=":
            r = self.check_eq(current, target)
        elif op == "<=":
            r = self.check_le(current, target)
        elif op == "<":
            r = self.check_lt(current, target)
        elif op == ">=":
            r = self.check_ge(current, target)
        elif op == ">":
            r = self.check_gt(current, target)
        else:
            perror("unsupported operator", op)
            return RET_ERR
        if verbose:
            if r == RET_OK:
                pinfo("property %s current value %s is %s %s. on target." % (prop, current, op, target))
            else:
                pinfo("property %s current value %s is not %s %s." % (prop, current, op, target))
        return r

    def check_zbin(self):
        return which(self.zbin)

    def check(self):
        if not self.check_zbin():
            pinfo(self.zbin, "not found")
            return RET_NA
        r = 0
        for d in self.data:
            r |= self.check_zprop(d, verbose=True)
        return r

    def fix(self):
        if not self.check_zbin():
            pinfo(self.zbin, "not found")
            return RET_NA
        r = 0
        for d in self.data:
            r |= self.fix_zprop(d)
        return r

if __name__ == "__main__":
    main(CompZprop)

   07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000b00000000TRAILER!!!                                                                                                                                                                                                                                                                                                                                                                                                        